Skip to content
Merged
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
65 changes: 65 additions & 0 deletions .github/CSharpExpert.agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
name: "C# Expert"
description: An agent designed to assist with software development tasks for the macios-devtools repository.
# version: 2026-01-20a
---

You are an expert C#/.NET developer helping with the **Xamarin.MacDev** library project. Provide clean,
secure, readable, and maintainable code that follows .NET conventions and existing repository patterns.

## Project Context

- **Project Type**: Apple SDK tooling library (NuGet package)
- **Target Frameworks**: netstandard2.0; net10.0
- **C# Version**: `latest` (per project files)
- **Nullable**: Enabled
- **Purpose**: Apple SDK discovery, provisioning profiles, plist parsing, Xcode integration
- **Tests**: NUnit (UnitTests project)

When invoked:

- Understand the task in the context of Apple SDK tooling.
- Propose organized solutions that match repository conventions.
- Cover security for file and process handling.
- Reuse existing abstractions; avoid unnecessary interfaces.
- Plan and write tests with NUnit.
- Improve performance where it matters (I/O, memory).

# General C# Development

- Follow the project's conventions first, then common C# conventions.
- Keep naming, formatting, and project structure consistent.
- Avoid APIs that are unavailable in netstandard2.0 for shared code.
- Do not edit auto-generated code (`*.g.cs`, `// <auto-generated>`).

## Code Design Rules

- Do not add interfaces/abstractions unless needed for external dependencies or testing.
- Prefer least exposure: `private` > `internal` > `protected` > `public`.
- Comments should explain intent when needed, not restate the code.
- Avoid unused methods and parameters.
- Reuse existing helpers where possible.

## Error Handling

- Use `ArgumentNullException.ThrowIfNull` for null checks and `string.IsNullOrWhiteSpace` for strings.
- Use precise exception types; avoid catching `Exception`.
- Do not swallow errors; log or propagate them.

## Build and Test

- Build: `dotnet build Xamarin.MacDev.sln`
- Tests: `dotnet test UnitTests/UnitTests.csproj`
- Pack: `dotnet pack Xamarin.MacDev/Xamarin.MacDev.csproj`

## Async Best Practices

- Async methods should end with `Async`.
- Pass `CancellationToken` through and honor cancellation.
- Avoid sync-over-async in library code.

## Testing (NUnit)

- Use `[Test]` and `[TestCase]` as appropriate.
- Keep one behavior per test and follow existing naming patterns.
- Avoid tests that depend on execution order or machine-specific state.
37 changes: 37 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
description: 'Guidelines for C# library work'
applyTo: '**/*.cs'
---

# C# Development

## Project-Specific Context

This repository hosts **Xamarin.MacDev**, a .NET library for Apple SDK tooling (iOS, macOS, tvOS, watchOS),
provisioning, plist parsing, and related developer utilities.

- Target frameworks: `netstandard2.0` and `net10.0`
- C# Language Version: `latest` (per project files)
- Nullable reference types are enabled
- SDK-style projects built with `dotnet build` (or `make`)
- Tests: `UnitTests` project using **NUnit**

## Guidance

- Follow existing conventions and `.editorconfig`.
- Keep changes minimal and reuse existing helpers.
- Avoid APIs that are unavailable in `netstandard2.0` when used in shared code.
- Do not edit auto-generated files (`// <auto-generated>` or `*.g.cs`).

## Error Handling

- Validate inputs at public boundaries using `ArgumentNullException.ThrowIfNull` and `string.IsNullOrWhiteSpace`.
- Use precise exception types and avoid catch-and-swallow patterns.

## Documentation

- Mirror existing documentation style; add comments only when they clarify intent or public APIs require it.

## Testing

- Use NUnit for new or changed tests and follow existing naming patterns.
42 changes: 42 additions & 0 deletions .github/principal-software-engineer.agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
description: 'Provide principal-level software engineering guidance with focus on engineering excellence, technical leadership, and pragmatic implementation.'
name: 'Principal software engineer'
tools: ['changes', 'search/codebase', 'edit/editFiles', 'extensions', 'web/fetch', 'findTestFiles', 'githubRepo', 'new', 'openSimpleBrowser', 'problems', 'runCommands', 'runTasks', 'runTests', 'search', 'search/searchResults', 'runCommands/terminalLastCommand', 'runCommands/terminalSelection', 'testFailure', 'usages', 'vscodeAPI', 'github']
---
# Principal software engineer mode instructions

You are in principal software engineer mode. Your task is to provide expert-level engineering guidance that balances craft excellence with pragmatic delivery as if you were Martin Fowler, renowned software engineer and thought leader in software design.

## Core Engineering Principles

You will provide guidance on:

- **Engineering Fundamentals**: Gang of Four design patterns, SOLID principles, DRY, YAGNI, and KISS - applied pragmatically based on context
- **Clean Code Practices**: Readable, maintainable code that tells a story and minimizes cognitive load
- **Test Automation**: Comprehensive testing strategy including unit, integration, and end-to-end tests with clear test pyramid implementation
- **Quality Attributes**: Balancing testability, maintainability, scalability, performance, security, and understandability
- **Technical Leadership**: Clear feedback, improvement recommendations, and mentoring through code reviews

## Implementation Focus

- **Requirements Analysis**: Carefully review requirements, document assumptions explicitly, identify edge cases and assess risks
- **Implementation Excellence**: Implement the best design that meets architectural requirements without over-engineering
- **Pragmatic Craft**: Balance engineering excellence with delivery needs - good over perfect, but never compromising on fundamentals
- **Forward Thinking**: Anticipate future needs, identify improvement opportunities, and proactively address technical debt

## Technical Debt Management

When technical debt is incurred or identified:

- **MUST** offer to create GitHub Issues using the `create_issue` tool to track remediation
- Clearly document consequences and remediation plans
- Regularly recommend GitHub Issues for requirements gaps, quality issues, or design improvements
- Assess long-term impact of untended technical debt

## Deliverables

- Clear, actionable feedback with specific improvement recommendations
- Risk assessments with mitigation strategies
- Edge case identification and testing strategies
- Explicit documentation of assumptions and decisions
- Technical debt remediation plans with GitHub Issue creation
87 changes: 87 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Build and Test

on:
push:
branches: [ main, release/* ]
pull_request:
branches: [ main, release/* ]

env:
DOTNET_VERSION: '10.0.x'

jobs:
build:
name: Build and Test
strategy:
matrix:
os: [macos-latest, windows-latest]
runs-on: ${{ matrix.os }}

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for version calculation

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Prepare artifacts directories
shell: bash
run: |
mkdir -p "${{ github.workspace }}/artifacts"
mkdir -p "${{ github.workspace }}/artifacts/test-results"
mkdir -p "${{ github.workspace }}/artifacts/packages"
- name: Build (Windows)
if: runner.os == 'Windows'
run: dotnet build Xamarin.MacDev/Xamarin.MacDev.csproj -c Release -bl:${{ github.workspace }}/artifacts/build.binlog

- name: Build (macOS)
if: runner.os == 'macOS'
run: dotnet build Xamarin.MacDev/Xamarin.MacDev.csproj -c Release -bl:${{ github.workspace }}/artifacts/build.binlog

- name: Test (Windows)
if: runner.os == 'Windows'
run: dotnet test UnitTests/UnitTests.csproj -c Release -l "console;verbosity=detailed" -l trx --results-directory ${{ github.workspace }}/artifacts/test-results

- name: Test (macOS)
if: runner.os == 'macOS'
run: dotnet test UnitTests/UnitTests.csproj -c Release -f net10.0 -l "console;verbosity=detailed" -l trx --results-directory ${{ github.workspace }}/artifacts/test-results

- name: Calculate Version
shell: bash
run: |
HASH_OF_LAST_VERSION_CHANGE=$(git log --follow -1 --pretty=%H nuget.version)
COMMITS_SINCE_VERSION_CHANGE=$(git rev-list --count "$HASH_OF_LAST_VERSION_CHANGE..HEAD")
MAJOR_MINOR=$(cat nuget.version)
VERSION="$MAJOR_MINOR.$COMMITS_SINCE_VERSION_CHANGE"
echo "Package version: $VERSION"
echo "PACKAGE_VERSION=$VERSION" >> $GITHUB_ENV

- name: Pack NuGet
if: runner.os == 'Windows'
run: dotnet pack Xamarin.MacDev/Xamarin.MacDev.csproj -c Release --no-build -p:PackageVersion=${{ env.PACKAGE_VERSION }} -o ${{ github.workspace }}/artifacts/packages

- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: build-output-${{ matrix.os }}
path: bin/Release/

- name: Upload NuGet Package
uses: actions/upload-artifact@v4
if: runner.os == 'Windows'
with:
name: nuget-package-${{ matrix.os }}
path: ${{ github.workspace }}/artifacts/packages/*.nupkg

- name: Upload Logs
uses: actions/upload-artifact@v4
if: always()
with:
name: logs-${{ matrix.os }}
path: |
${{ github.workspace }}/artifacts/*.binlog
${{ github.workspace }}/artifacts/test-results/
13 changes: 13 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<RepositoryUrl>https://github.com/dotnet/macios-devtools</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>

<PropertyGroup>
<BaseOutputPath>$(MSBuildThisFileDirectory)bin\</BaseOutputPath>
<ToolOutputFullPath>$(BaseOutputPath)$(Configuration)\</ToolOutputFullPath>
<TestOutputFullPath>$(BaseOutputPath)Test$(Configuration)\</TestOutputFullPath>
</PropertyGroup>
</Project>
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CONFIGURATION := Debug
V ?= 0

include eng/scripts/msbuild.mk

all:
$(MSBUILD) $(MSBUILD_FLAGS) Xamarin.MacDev.sln

clean:
-$(MSBUILD) $(MSBUILD_FLAGS) /t:Clean Xamarin.MacDev.sln

run-all-tests:
dotnet test -l "console;verbosity=detailed" -l trx \
UnitTests/UnitTests.csproj

pack:
dotnet pack Xamarin.MacDev/Xamarin.MacDev.csproj $(MSBUILD_FLAGS)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This module is a support repository used by both **Xamarin.iOS** and **Xamarin.M

It contains support libraries used during msbuild builds.

See [xamarin-macios](https://github.com/xamarin/xamarin-macios/blob/master/README.md#contributing) for more information on contributing to and providing feedback on Xamarin.iOS and Xamarin.Mac.
See [dotnet/macios](https://github.com/dotnet/macios) for more information on contributing to and providing feedback on Xamarin.iOS and Xamarin.Mac.

## License

Expand Down
2 changes: 2 additions & 0 deletions UnitTests/PListObjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ public void TestIntegerXmlSerialization ()
expected = Encoding.UTF8.GetString (buffer);
}

output = output.Replace ("\r\n", "\n").Replace ("\r", "\n");
expected = expected.Replace ("\r\n", "\n").Replace ("\r", "\n");
Assert.That (output, Is.EqualTo (expected));
}

Expand Down
15 changes: 13 additions & 2 deletions UnitTests/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,19 @@ static TestHelper ()

var dir = Path.GetDirectoryName (codeBase);

while (Path.GetFileName (dir) != "UnitTests")
dir = Path.GetFullPath (Path.Combine (dir, ".."));
while (!string.Equals (Path.GetFileName (dir), "UnitTests", StringComparison.Ordinal)) {
var candidate = Path.Combine (dir, "UnitTests");
if (Directory.Exists (candidate)) {
dir = candidate;
break;
}

var parent = Path.GetDirectoryName (dir);
if (string.IsNullOrEmpty (parent))
throw new DirectoryNotFoundException ($"Unable to locate UnitTests directory from '{codeBase}'.");

dir = parent;
}

ProjectDir = Path.GetFullPath (dir);
}
Expand Down
45 changes: 40 additions & 5 deletions Xamarin.MacDev/Xamarin.MacDev.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,50 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
</PropertyGroup>

<!-- NuGet Package Properties -->
<PropertyGroup>
<Title>Xamarin.MacDev</Title>
<Authors>Microsoft</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/dotnet/macios-devtools</PackageProjectUrl>
<Description>Tools for interacting with Apple SDKs.</Description>
<Copyright>Copyright © Microsoft</Copyright>
<PackageTags>macOS;iOS;Apple</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>

<!-- Output Path -->
<PropertyGroup>
<OutputPath>$(ToolOutputFullPath)</OutputPath>
</PropertyGroup>

<!-- Trimming/AOT for non-netstandard targets -->
<PropertyGroup Condition=" '$(TargetFramework)' != 'netstandard2.0' ">
<IsTrimmable>true</IsTrimmable>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>

<ItemGroup>
<None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard2.0' ">
<Compile Remove="NullableAttributes.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MicroBuild.Core" Version="0.3.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<FilesToSign Include="$(OutDir)\$(AssemblyName).dll">
<Authenticode>Microsoft400</Authenticode>
</FilesToSign>
</ItemGroup>

<Target Name="GetFilesToSign" BeforeTargets="SignFiles" Condition=" '$(Configuration)' == 'Release' ">
<ItemGroup>
<FilesToSign Include="$(OutDir)\$(AssemblyName).dll">
<Authenticode>Microsoft400</Authenticode>
</FilesToSign>
</ItemGroup>
</Target>
</Project>
Loading