Skip to content

Commit 552d0d2

Browse files
authored
Add .github/copilot-instructions.md for Copilot cloud agent onboarding (#58)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: josesimoes <1881520+josesimoes@users.noreply.github.com> ***NO_CI***
1 parent 2cffc66 commit 552d0d2

1 file changed

Lines changed: 209 additions & 0 deletions

File tree

.github/copilot-instructions.md

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Copilot Instructions for nanoFramework.DependencyInjection
2+
3+
## Project Overview
4+
5+
This repository implements a **Dependency Injection (DI) / Inversion of Control (IoC) container** for [.NET nanoFramework](https://www.nanoframework.net/) — a free, open-source platform that enables C# development for constrained embedded devices (microcontrollers). The library mirrors the API of the official [Microsoft.Extensions.DependencyInjection](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection) package as closely as possible, with adaptations for the limitations of nanoFramework.
6+
7+
The NuGet package is published as **`nanoFramework.DependencyInjection`** and is a dependency of [nanoFramework.Hosting](https://github.com/nanoframework/nanoFramework.Hosting).
8+
9+
## Repository Structure
10+
11+
```
12+
/
13+
├── nanoFramework.DependencyInjection/ # Main library project
14+
│ ├── nanoFramework.DependencyInjection.nfproj # nanoFramework project file (not .csproj)
15+
│ ├── Microsoft/Extensions/DependencyInjection/ # Core DI types (mirrors MS.Extensions.DI namespace)
16+
│ │ ├── ServiceCollection.cs
17+
│ │ ├── ServiceDescriptor.cs
18+
│ │ ├── ServiceLifetime.cs
19+
│ │ ├── ServiceProvider.cs
20+
│ │ ├── ServiceProviderEngine.cs
21+
│ │ ├── ServiceProviderEngineScope.cs
22+
│ │ ├── ServiceProviderOptions.cs
23+
│ │ ├── ActivatorUtilities.cs
24+
│ │ ├── ServiceCollectionServiceExtensions.cs
25+
│ │ ├── ServiceCollectionDescriptorExtensions.cs
26+
│ │ ├── ServiceCollectionContainerBuilderExtensions.cs
27+
│ │ ├── ServiceProviderServiceExtensions.cs
28+
│ │ ├── ServiceScope.cs
29+
│ │ ├── TypeExtensions.cs
30+
│ │ └── (interfaces: IServiceCollection, IServiceScope, IServiceProviderIsService)
31+
│ └── System/ # nanoFramework-specific polyfills
32+
│ ├── Activator.cs # Custom Activator (uses reflection)
33+
│ ├── AggregateException.cs # Custom AggregateException
34+
│ └── IServiceProvider.cs
35+
├── tests/ # Unit tests
36+
│ ├── nanoFramework.DependencyInjection.UnitTests.nfproj
37+
│ ├── nano.runsettings # Test run settings (IsRealHardware=False → nanoCLR simulator)
38+
│ ├── Fakes/ # Test fakes and stubs
39+
│ ├── DependencyInjectionTests.cs
40+
│ ├── ServiceProviderTests.cs
41+
│ ├── ServiceCollectionTests.cs
42+
│ ├── ServiceCollectionDescriptorExtensionsTests.cs
43+
│ ├── ServiceProviderExtensionsTests.cs
44+
│ ├── ActivatorTests.cs
45+
│ ├── ActivatorUtilitiesTests.cs
46+
│ └── AggregateExceptionTests.cs
47+
├── nanoFramework.DependencyInjection.sln # Visual Studio solution
48+
├── nanoFramework.DependencyInjection.nuspec # NuGet package specification
49+
├── azure-pipelines.yml # Primary CI/CD (Azure Pipelines)
50+
├── version.json # Nerdbank.GitVersioning config (version: 1.1)
51+
├── NuGet.Config # NuGet source (nuget.org only)
52+
└── .github/workflows/ # GitHub Actions (PR checks, dependency updates)
53+
├── pr-checks.yml # Checks package lock and NuGet versions on PRs
54+
├── update-dependencies.yml
55+
├── update-dependencies-develop.yml
56+
└── generate-changelog.yml
57+
```
58+
59+
## Critical Platform Constraints
60+
61+
**.NET nanoFramework is NOT standard .NET.** The code targets an embedded runtime with significant restrictions:
62+
63+
1. **No generics in the public API** — All methods that would use `<T>` in standard .NET use `Type` parameters instead. Example: `serviceCollection.AddSingleton(typeof(IMyService), typeof(MyService))` instead of `AddSingleton<IMyService, MyService>()`.
64+
65+
2. **Limited reflection**`Type.GetConstructor()`, `Type.GetConstructors()`, `ConstructorInfo.Invoke()` are available but behaviour can differ from full .NET. `GetType()` is available on instances.
66+
67+
3. **No struct support in DI container**`struct` types cannot be used as service registrations. The constructor for `struct` types fails in `typeof(T).GetConstructor()`. See the commented-out test `ServicesRegisteredWithImplementationTypeForStructSingletonServices` and the issue reference: https://github.com/nanoframework/Home/issues/1085.
68+
69+
4. **No array-typed constructor parameters** — Types that have `byte[]` or other array parameters in their constructors will cause a null exception during activation.
70+
71+
5. **No full BCL** — Only a subset of the .NET base class library is available via `nanoFramework.CoreLibrary` (`mscorlib`). Standard types like `ArrayList` (not `List<T>`), `Hashtable`, etc. are used.
72+
73+
6. **Namespace `System.Collections`** — Use `ArrayList` and `Hashtable` instead of generic collections.
74+
75+
7. **Factory delegates use a custom delegate**`ImplementationFactoryDelegate` (`delegate object ImplementationFactoryDelegate(IServiceProvider serviceProvider)`) is used instead of `Func<IServiceProvider, object>`.
76+
77+
## Build Environment
78+
79+
> **⚠️ IMPORTANT: The nanoFramework build system is Windows-only.**
80+
81+
The project uses `.nfproj` files (nanoFramework project format), which require:
82+
- **Windows OS** with the nanoFramework Visual Studio extension or MSBuild extensions installed at `$(MSBuildExtensionsPath)\nanoFramework\v1.0\`
83+
- **MSBuild** (via Visual Studio or .NET SDK for Windows)
84+
- The CI/CD pipeline (`azure-pipelines.yml`) uses `windows-latest` runners
85+
86+
**You cannot build or run tests in a Linux/macOS environment** (the standard GitHub Copilot cloud agent environment). Attempting `dotnet build` on `.nfproj` files will fail because the `NFProjectSystem.props`/`.targets` files are not present.
87+
88+
### What you CAN do in the cloud agent environment:
89+
- Read, analyze, and edit all source files
90+
- Update documentation (README.md, CHANGELOG.md, XML doc comments)
91+
- Add/modify test files and source files
92+
- Review and update NuGet package versions in `packages.config`
93+
- Update GitHub Actions workflows in `.github/workflows/`
94+
- Make structural changes that will be validated by CI on Azure Pipelines
95+
96+
### What requires Windows to validate:
97+
- Compiling the project
98+
- Running unit tests (via nanoCLR simulator with `nanoFramework.TestFramework`)
99+
- Verifying NuGet package restore
100+
101+
## Testing
102+
103+
Tests use **nanoFramework.TestFramework** (not MSTest, NUnit, or xUnit). Key points:
104+
105+
- Test classes are decorated with `[TestClass]` and methods with `[TestMethod]` from `nanoFramework.TestFramework`
106+
- Assertions use `Assert.IsType()`, `Assert.AreEqual()`, `Assert.AreNotSame()`, `Assert.IsNull()`, `Assert.IsNotNull()`, `Assert.ThrowsException()`, etc.
107+
- Tests run on the **nanoCLR simulator** (not real hardware) — configured in `tests/nano.runsettings` via `<IsRealHardware>False</IsRealHardware>`
108+
- Test timeout: 120 seconds (`<TestSessionTimeout>120000</TestSessionTimeout>`)
109+
- The Azure Pipelines build passes `unitTestRunsettings: '$(System.DefaultWorkingDirectory)\tests\nano.runsettings'` to the test runner
110+
111+
## Service Lifetimes
112+
113+
The library supports three service lifetimes (same as standard .NET):
114+
115+
| Lifetime | Behavior |
116+
|----------|----------|
117+
| `Singleton` | One instance shared across the entire application lifetime |
118+
| `Transient` | A new instance created every time the service is requested |
119+
| `Scoped` | One instance per scope (created via `serviceProvider.CreateScope()`) |
120+
121+
## Key API Patterns
122+
123+
```csharp
124+
// Registration (no generics — use Type parameters)
125+
var serviceProvider = new ServiceCollection()
126+
.AddSingleton(typeof(IMyService), typeof(MyServiceImpl))
127+
.AddTransient(typeof(IAnotherService), typeof(AnotherServiceImpl))
128+
.AddScoped(typeof(IScopedService), typeof(ScopedServiceImpl))
129+
.BuildServiceProvider();
130+
131+
// Resolution
132+
var service = (IMyService)serviceProvider.GetService(typeof(IMyService));
133+
var required = (IMyService)serviceProvider.GetRequiredService(typeof(IMyService));
134+
object[] services = serviceProvider.GetServices(typeof(IMyService));
135+
136+
// Scoped resolution
137+
using (var scope = serviceProvider.CreateScope())
138+
{
139+
var scoped = scope.ServiceProvider.GetService(typeof(IScopedService));
140+
}
141+
142+
// Factory registration
143+
services.AddSingleton(typeof(IMyService), (IServiceProvider sp) => new MyServiceImpl());
144+
145+
// Instance registration
146+
services.AddSingleton(typeof(IMyService), existingInstance);
147+
148+
// ActivatorUtilities (create without registering)
149+
var instance = (MyClass)ActivatorUtilities.CreateInstance(provider, typeof(MyClass), arg1, arg2);
150+
151+
// Validation options
152+
var provider = services.BuildServiceProvider(new ServiceProviderOptions
153+
{
154+
ValidateOnBuild = true, // Validate all services can be constructed
155+
ValidateScopes = true // Validate scoped services never resolved from root
156+
});
157+
```
158+
159+
## NuGet Package Management
160+
161+
This project uses the **old-style `packages.config`** NuGet format (not `PackageReference`). To update packages:
162+
- Edit `packages.config` in the relevant project directory
163+
- Edit the corresponding `<HintPath>` references in the `.nfproj` file
164+
- The lock file (`packages.lock.json`) is enforced in CI via `<RestoreLockedMode>true</RestoreLockedMode>`
165+
166+
Main dependencies:
167+
- `nanoFramework.CoreLibrary` (v1.17.11) — the nanoFramework mscorlib
168+
- `Nerdbank.GitVersioning` (v3.9.50) — automatic versioning from git history
169+
- `nanoFramework.TestFramework` (v3.0.77) — test framework (tests project only)
170+
171+
## Versioning
172+
173+
Versioning is managed by **Nerdbank.GitVersioning** via `version.json`. The current version base is `1.1`. Releases are tagged `v*` and trigger publish jobs in the Azure Pipelines CI. Preview packages are published from non-release branches.
174+
175+
## Code Style
176+
177+
- File header: `// Copyright (c) .NET Foundation and Contributors\n// See LICENSE file in the project root for full license information.`
178+
- Namespace: Main library uses `Microsoft.Extensions.DependencyInjection` and `System` (polyfills); tests use `nanoFramework.DependencyInjection.UnitTests`
179+
- XML doc comments on all public API members
180+
- `lock (_syncLock)` pattern used in `ServiceCollection` for thread safety
181+
- `.editorconfig` only sets `dotnet_analyzer_diagnostic.severity = silent`
182+
- Assembly is strong-named via `key.snk`
183+
184+
## Known Issues and Workarounds
185+
186+
1. **Struct types in DI**: `struct` types cannot be registered or resolved. This is a nanoFramework limitation (issue [#1085](https://github.com/nanoframework/Home/issues/1085)). The affected test is commented out.
187+
188+
2. **Array constructor parameters**: Types with array-typed constructor parameters (e.g., `byte[]`) will throw a null exception during activation. Document this limitation in any affected code/tests.
189+
190+
3. **`GetImplementationType()` with factory**: When a service is registered only via a factory (`ImplementationFactory`), `GetImplementationType()` returns `null` because nanoFramework lacks generic type argument introspection. See the commented-out code in `ServiceDescriptor.cs`.
191+
192+
4. **`RestoreLockedMode` in CI**: The `packages.lock.json` is enforced in CI. If you update package versions in `packages.config`, the lock file must be regenerated on a Windows machine before the CI will pass.
193+
194+
5. **Building in cloud agent**: As noted above, building `.nfproj` files requires the nanoFramework project system on Windows. Code changes should be validated by the Azure Pipelines CI after merging or creating a PR.
195+
196+
## CI/CD
197+
198+
- **Primary CI**: Azure Pipelines (`azure-pipelines.yml`) — builds, runs tests, publishes NuGet packages
199+
- **PR Checks** (GitHub Actions): `.github/workflows/pr-checks.yml` — verifies `packages.lock.json` consistency and that NuGet packages are up to date
200+
- **Dependency updates**: Automated via `.github/workflows/update-dependencies.yml` and `update-dependencies-develop.yml`
201+
- **Downstream dependents**: `nanoFramework.Hosting` is automatically updated when a new release is published
202+
203+
## Related Resources
204+
205+
- [nanoFramework Home](https://github.com/nanoframework/Home)
206+
- [nanoFramework.Hosting](https://github.com/nanoframework/nanoFramework.Hosting) — uses this library
207+
- [DependencyInjection Samples](https://github.com/nanoframework/Samples/tree/main/samples/DependencyInjection)
208+
- [Contributing Guide](https://github.com/nanoframework/Home/blob/main/CONTRIBUTING.md)
209+
- [Discord Community](https://discord.gg/gCyBu8T)

0 commit comments

Comments
 (0)