Skip to content
Open
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
18 changes: 18 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,22 @@
<Compile Include="$(MSBuildThisFileDirectory)AssemblyInfo.cs" />
</ItemGroup>

<!-- JetBrains.Annotations: the "outer" (consumer-facing) layer gets [assembly: PublicAPI] so
Rider/ReSharper treat its entire public surface as intentional API and never flag it as
unused. This list is the single source of truth for what counts as an outer layer — inner
layers (MSBuildTasks, SourceGenerators, the Cli/Migrate tools, Migrate.Analyzers) are NOT
blanket-marked; any genuinely-exported type there is annotated with [PublicAPI] in source.
A project can opt out by setting <FalloutPublicApi>false</FalloutPublicApi> before this import. -->
<PropertyGroup>
<_FalloutOuterApiAssemblies>;Fallout.Common;Fallout.Build;Fallout.Build.Shared;Fallout.Components;Fallout.Core;Fallout.Tooling;Fallout.ProjectModel;Fallout.Utilities;Fallout.Utilities.IO.Compression;Fallout.Utilities.IO.Globbing;Fallout.Utilities.Net;Fallout.Utilities.Text.Json;Fallout.Utilities.Text.Yaml;Fallout.Solution;Fallout.NuGet.Analysis;Fallout.Tooling.Generator;Nuke.Common;Nuke.Build;Nuke.Components;</_FalloutOuterApiAssemblies>
<FalloutPublicApi Condition="'$(FalloutPublicApi)' == '' and $(_FalloutOuterApiAssemblies.Contains(';$(MSBuildProjectName);'))">true</FalloutPublicApi>
</PropertyGroup>

<!-- The reference flows to consumers (no PrivateAssets) so a consumer's Rider also sees the
annotations baked into our public surface. CPM is off for build/_build.csproj, so guard on it. -->
<ItemGroup Condition="'$(FalloutPublicApi)' == 'true' and '$(ManagePackageVersionsCentrally)' != 'false'">
<PackageReference Include="JetBrains.Annotations" />
<AssemblyAttribute Include="JetBrains.Annotations.PublicAPIAttribute" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageVersion Include="Glob" Version="1.1.9" />
<PackageVersion Include="HtmlAgilityPack" Version="1.11.71" />
<PackageVersion Include="Humanizer" Version="3.0.1" />
<PackageVersion Include="JetBrains.Annotations" Version="2026.2.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="10.0.0" />
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.7.115" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
Expand Down
52 changes: 52 additions & 0 deletions docs/api-encapsulation-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# API encapsulation audit

Companion to the `[PublicAPI]` annotation pass (PR #6). Stamping `[assembly: PublicAPI]`
on the outer (consumer-facing) layer declares each assembly's **entire** public surface as
intentional API. This audit records public types that are `public` by accident and the
decisions taken. (GitHub issues are disabled on this fork, so this doc is the tracker.)

> **Caveat that drives every call here:** repo-only grep is insufficient for a framework.
> A type with zero in-repo references can be internal plumbing **or** an extensibility
> surface only external consumers touch. Verify intent (NUKE history, `public`/`protected
> virtual` reachability) before flipping anything in an outer assembly.

## Tier 1 — done (non-breaking, inner-layer assemblies)

Flipped to `internal` in PR #6 (no consumer-facing API in these assemblies):

| Type | Assembly | Note |
|---|---|---|
| `TaskItemExtensions` | `Fallout.MSBuildTasks` | not packed; in-assembly helper |
| `CodeAnalysisExtensions` | `Fallout.SourceGenerators` | not packed; in-assembly helper |
| `Migration` + nested `Summary` | `Fallout.Migrate` | tool exe; used only by `Program` + tests (IVT). `Migration` internalized with `Summary` to keep accessibility consistent (CS0050) |

## Tier 2 — deferred (breaking; batch to the yearly major cut)

High-confidence implementation details, but in **outer/packed** assemblies — flipping is
breaking, so it follows the breaking-change flow (gate behind `[Experimental(...)]` or hold
on a topic branch; CHANGELOG + `breaking-change` label at PR time).

**`Fallout.Build`**
- `InMemorySink` (`Logging.cs`) — nested log sink, in-assembly only
- `ConsoleUtility` (`Utilities/ConsoleUtility.cs`) — console I/O helper, in-assembly only
- `Terminal` + `Terminal.Rider` / `Terminal.VSCode` / `Terminal.VisualStudio` (`Terminal.cs`) — env-detection markers

**`Fallout.Persistence.Solution`** (csproj already documents intent to narrow these)
- `StringTable`, `PathShim`, `Extensions`

Before flipping: confirm no external consumer usage and no extensibility reachability for each.

## Tier 3 — keep public (intentional extensibility; not candidates)

The ~49 CI Configuration types (`*Step` / `*Trigger` / `*Job` / `*Parameter` / `*Dependency`
+ abstract bases under `Fallout.Common/CI/**/Configuration/`) and abstract attribute bases
(`FileSystemGlobbingAttributeBase`, `AzureKeyVaultAttributeBase`). These are unreferenced
in-repo precisely *because* only external consumers use them: consumers subclass e.g.
`GitHubActionsAttribute`, override `public override ConfigurationEntity GetConfiguration(...)`,
and build these objects. The blanket `[assembly: PublicAPI]` is correct for them — it stops
Rider flagging them as unused. **No action.**

## Clean

`Fallout.Components`, `Fallout.Core`, `Fallout.Tooling`, `Fallout.ProjectModel`, all
`Fallout.Utilities*`, and the `Fallout.Solution` facade audited clean — no accidental publics.
2 changes: 1 addition & 1 deletion src/Fallout.MSBuildTasks/TaskItemExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Fallout.MSBuildTasks;

public static class TaskItemExtensions
internal static class TaskItemExtensions
{
public static string GetMetadataOrNull(this ITaskItem taskItem, string metdataName)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Fallout.Migrate/Migration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Fallout.Migrate;

public sealed class Migration
internal sealed class Migration
{
private readonly string _rootDirectory;
private readonly bool _dryRun;
Expand Down Expand Up @@ -144,7 +144,7 @@ private string RelativePath(string absolute) =>

private void Log(string line) => _log.WriteLine(line);

public sealed class Summary
internal sealed class Summary
{
public int FilesChanged { get; set; }
public int EditCount { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion src/Fallout.SourceGenerators/CodeAnalysisExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Fallout.SourceGenerators;

public static class CodeAnalysisExtensions
internal static class CodeAnalysisExtensions
{
public static IEnumerable<INamespaceSymbol> GetAllNamespaces(this INamespaceSymbol namespaceSymbol)
{
Expand Down
Loading