Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spec proposal for opt-in feature where PrivateAssets flag option indepdent from IncludeAssets/ExcludeAssets options #12313

Closed
Closed
Changes from 8 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
0864d28
Spec proposal for privateAssets not eclipsed by includeassets/exclude…
erdembayar Dec 7, 2022
5cd02a1
Improve example
erdembayar Dec 8, 2022
19bd26e
Address PR comment
erdembayar Dec 8, 2022
58f030a
Rename property to PrivateAssetIndependent
erdembayar Dec 9, 2022
d231ef3
Add project2project scenario
erdembayar Dec 9, 2022
b272b0e
Take Andy's suggestion
erdembayar Dec 9, 2022
fdf8ee8
Clarify flow table
erdembayar Dec 9, 2022
bf84bb4
P2P consumption example
erdembayar Dec 9, 2022
ed3370b
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Dec 15, 2022
188b8dc
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Dec 15, 2022
6bf5644
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Dec 15, 2022
9563247
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Dec 15, 2022
68f52a1
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Dec 15, 2022
4cd21af
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Dec 15, 2022
a1f9f46
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Dec 15, 2022
3338e2c
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Dec 15, 2022
dae50f7
Take AArnott suggestion
erdembayar Dec 15, 2022
c76de07
Address Fernando's comment
erdembayar Dec 15, 2022
e9a8465
Correct pack example
erdembayar Dec 15, 2022
148c65f
Add Alternative idea
erdembayar Dec 15, 2022
72bcb2c
Address Andy's comment
erdembayar Dec 19, 2022
2b37bc5
Rename property
erdembayar Jan 10, 2023
271e53b
Add example
erdembayar Feb 16, 2023
1f7b433
Address Nikolche's comments
erdembayar Apr 4, 2023
001fad0
PublicAssets control
erdembayar Apr 5, 2023
a36b5f0
Change to ExcludedAssetsFlow boolean metadata
erdembayar Apr 7, 2023
e24fbf5
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Apr 7, 2023
3db6950
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Apr 7, 2023
7311350
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Apr 7, 2023
8fef1e9
Address remaining comment by Andrew
erdembayar Apr 7, 2023
1d3d357
Correct typos
erdembayar Apr 7, 2023
c9b9bc1
Address remaining comments by Nikolche
erdembayar Apr 7, 2023
df9cca9
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Apr 10, 2023
0f4b052
Nikolche shorten comment
erdembayar Apr 10, 2023
8693e02
Update proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
erdembayar Apr 10, 2023
0d53d37
Other comments
erdembayar Apr 10, 2023
1af7f27
More feedback address
erdembayar Apr 10, 2023
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
238 changes: 238 additions & 0 deletions proposed/2022/PrivateAssetIndepdentFromExcludeAsset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# Opt-in experience for PrivateAssets and ExcludeAssets options are independent

- Author Name <https://github.com/erdembayar>
- Start Date 2022-12-07
- GitHub Issue <https://github.com/NuGet/Home/issues/6938>
- GitHub PR <https://github.com/NuGet/NuGet.Client/pull/4976>

## Summary

<!-- One-paragraph description of the proposal. -->
Currently asset consumption via `PrivateAssets` option experience for assets from transitive package in a project/package is not deterministic for parent consuming project, here `PrivateAssets` option calculation is not independent from `IncludeAssets/ExcludeAssets` option.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
This proposal introduces new a `PrivateAssetIndependent` opt-in property to make `PrivateAssets` option independent from `IncludeAssets/ExcludeAssets` option.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

### Consumption via package reference
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

The `IncludeAssets`/`ExcludeAssets`, and `PrivateAssets` metadata on `PackageReference` items control two different features. Firstly, which assets from a package that are included in the current project. Secondly, whether the assets will be listed in the package's dependency assets, for those assets to flow transitively, if the project is packed.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

For example, `<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />` means "Include the default assets in the current project, but if packed, all of the assets are excluded, so this package will not be a dependency".
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

Another example, `<PackageReference Include="NuGet.Protocol" Version="6.4.0" PrivateAssets="compile" />` means "include the default assets in the current project, but if packed, the "compile" asset should be excluded from the package dependency, so that my package's dependencies do not leak APIs into projects using my package".

A third example, `<PackageReference Include="Microsoft.Build" Version="17.0" ExcludeAssets="runtime" />` means "in the current project, make the `compile` assets available (so both intellisense and the compiler can access APIs from the package), but `Microsoft.Build`'s dlls are not copied to `bin` on build (`runtime` assets), and if the project is packed, then `runtime` assets also be excluded for the `Microsoft.Build` dependency for any project that references the current project's package.

However, a scenario that is missing is "exclude a package asset from a current project, but do not exclude it from the dependency when the current project is packed", i.e
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
`<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.138-beta" PrivateAssets="none" IncludeAssets="none" />`.
This missing feature is more obvious when looking at a project/flow matrix below (see 3rd row), because `PrivateAssets` is not independent from `IncludeAssets`. We want to change that with opt-in property, it would let assets flow to the parent project on that case.

IncludeAssets|PrivateAssets|Flows transitively
--|--|--
yes|yes|yes
yes|no|no
no|yes|***no***
no|no|no

In above table `IncludeAssets` equals `yes` means `all` or `build` etc, but `no` means `none` or another non-intersecting asset with `PrivateAssets`.
`PrivateAssets` equals `yes` means `all` or `build` etc, but `no` means `none` or another non-intersecting asset with `IncludeAssets`.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

### Consumption via project reference
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

The above observation applies Project2Project references for parent project too when `restore/build` operation happens.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
Let's say the current project is `LibraryProj.csproj` and parent project has `<ProjectReference Include="..\LibraryProj.csproj" />` reference to it.

For example, `<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="none" />` in `LibraryProj.csproj` means "Include the default assets in the current project, but all assets flow to parent project".

Another example, `<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" IncludeAssets="none" />` in `LibraryProj.csproj` means "Consume the no assets in the current project, but default assets flow to parent project".
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

However if we combine above examples where both cases some assets flowing to parent project, `<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.138-beta" PrivateAssets="none" IncludeAssets="none" />`, currently experience is no asset flows to into parent project even though it requested all assets flow to parent project (see above table 3rd row), because `PrivateAssets` is not independent from `IncludeAssets`. We want to change that with opt-in property, it would let assets flow to the parent project on that case.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

## Motivation

<!-- Why are we doing this? What pain points does this solve? What is the expected outcome? -->
A package author should be able to deterministically decide which asset flow to parent consuming projects when this project is consumed as package. It enables parent projects to consume transitive packages without having to directly reference them, therefore it reduces the number of packages developers need to keep track of. So, it'll give more flexible control to the package author, not less.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
We couldn't make this default experience because it'll break customers who rely on current behavior.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

In addition to the above code author should be able to deterministically decide which asset flow to parent consuming projects when this project is consumed as project reference(P2Ps that reference this project).
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
If the current project consuming any packages, then it can be transitively consumed by parent project if code author wants to.

## Explanation
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

### Functional explanation

<!-- Explain the proposal as if it were already implemented and you're teaching it to another person. -->
<!-- Introduce new concepts, functional designs with real life examples, and low-fidelity mockups or pseudocode to show how this proposal would look. -->
The new ` PrivateAssetIndependent` property let only `PrivateAssets` option to decide which assets flow to consuming parent project, but doesn't affect restore experience for current project so there would be no change in `project.assets.json` lock file, i.e it doesn't change `IncludeAssets/ExcludeAssets` calculation for current project.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

It'll change how `compile, runtime, contentFiles, build, buildMultitargeting, buildTransitive, analyzers, native` dependencies flow into the projects consuming it via `PackageReference` and `ProjectReference` references.

For the following table assume `PrivateAssetIndependent` opt-in property is set `true` in current project, iterating possible scenarios (not full list) for consuming parent project.

| Asset flowing to parent project | New feature enabled | Possible downside |
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
|-----------------------|--------------|-----------------|
| compile | Expose new code, auto complete, intellicode, intellisense | Compile error due to naming ambiguity, run time error, NU1605 error |
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
| runtime | Let provide new runtime dependency | Run time error |
| build | Enable msbuild imports | Build fails due to property/target value change |
| analyzers | Code analyzers work | n/a |

Here is summary for what would change for consuming `parent` project, see 3rd row ***bold*** note.

Here `IncludeAssets` equals `yes` means `all` or `build` etc, but `no` means `none` or another non-intersecting asset with `PrivateAssets`.

Here `PrivateAssets` equals `yes` means `all` or `build` etc, but `no` means `none` or another non-intersecting asset with `IncludeAssets`.

Before change for any given `asset`:
IncludeAssets|PrivateAssets|Flows transitively
--|--|--
yes|yes|yes
yes|no|no
no|yes|***no***
no|no|no

After change for any given `asset`:
IncludeAssets|PrivateAsset|Flows transitively
--|--|--
yes|yes|yes
yes|no|no
no|yes|***yes***
no|no|no
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

#### Examples

##### Case 1 for PackageReference

Package reference in csproj file.

```.net
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<VersionSuffix>beta</VersionSuffix>
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
<PrivateAssetIndependent>True</PrivateAssetIndependent>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.138-beta" PrivateAssets="none" IncludeAssets="build" />
</ItemGroup>
```

Before change nuspec file:

```.net
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="Microsoft.Windows.CsWin32" version="0.2.138-beta" exclude="Runtime,Build,Native,Analyzers,BuildTransitive" />
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
</group>
</dependencies>
```

After change nuspec file:

```.net
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="Microsoft.Windows.CsWin32" version="0.2.138-beta" include="All" />
zivkan marked this conversation as resolved.
Show resolved Hide resolved
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
</group>
</dependencies>
```

##### Case 2 for PackageReference

Package reference in csproj file.

```.net
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<VersionSuffix>beta</VersionSuffix>
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
<PrivateAssetIndependent>True</PrivateAssetIndependent>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.138-beta" PrivateAssets="none" IncludeAssets="none" />
</ItemGroup>
```

Before change nuspec file:

```.net
<dependencies>
<group targetFramework=".NETStandard2.0" />
</dependencies>
```

After change nuspec file:

```.net
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="Microsoft.Windows.CsWin32" version="0.2.138-beta" include="All" />
</group>
</dependencies>
```

##### Case for Project to Project reference
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great to have the reverse example as well.

So 1 -> 2, where 1 has the flag enabled, but 2 is the one with privateassets.

There should be no effect in this case.

Another example is a 1 -> 2 -> 3, where 3 is the one with the flag and with the special private assets value. 1 and 2 are going to have different values for the package referenced in 3.

I'd use a compile example because build is in the default PrivateAssets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Added 2 more examples here. Could you please verify it for me?


Package reference in parent `ClassLibrary1` csproj file.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

```.net
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\refissue\refissue.csproj" />
</ItemGroup>
```

erdembayar marked this conversation as resolved.
Show resolved Hide resolved
```.net
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<VersionSuffix>beta</VersionSuffix>
<PrivateAssetIndependent>True</PrivateAssetIndependent>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.138-beta" PrivateAssets="none" ExcludeAssets="all" />
</ItemGroup>
```

Before changing, `Microsoft.Windows.CsWin32.props` file in `build` asset doesn't flow to ClassLibrary1.csproj.nuget.g.props file in obj folder.

After change, `Microsoft.Windows.CsWin32.props` file in `build` asset flow to ClassLibrary1.csproj.nuget.g.props file in obj folder.

### Technical explanation

We already have a [logic](hhttps://github.com/NuGet/NuGet.Client/blob/380415d812681ebf1c8aa0bc21533d4710514fc3/src/NuGet.Core/NuGet.Commands/CommandRunners/PackCommandRunner.cs#L577-L582) that combines `IncludeAssets/ExcludeAssets` options with `PrivateAssets` for pack operation, we just make logic depending on opt-in property.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

## Drawbacks

<!-- Why should we not do this? -->
- Might break some consuming projects, that is why it's opt-in feature. See table in #functional-explanation above for more details.

## Rationale and alternatives
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

<!-- Why is this the best design compared to other designs? -->
<!-- What other designs have been considered and why weren't they chosen? -->
<!-- What is the impact of not doing this? -->
- We could make it opt-in option in `nuget.config` file, but it doesn't give customer to option to opt in/out for per project level.
erdembayar marked this conversation as resolved.
Show resolved Hide resolved
erdembayar marked this conversation as resolved.
Show resolved Hide resolved

```.net
<config>
<add key="PrivateAssetIndependent" value="true" />
</config>
```

## Prior Art

<!-- What prior art, both good and bad are related to this proposal? -->
<!-- Do other features exist in other ecosystems and what experience have their community had? -->
<!-- What lessons from other communities can we learn from? -->
<!-- Are there any resources that are relevant to this proposal? -->

## Unresolved Questions

<!-- What parts of the proposal do you expect to resolve before this gets accepted? -->
<!-- What parts of the proposal need to be resolved before the proposal is stabilized? -->
<!-- What related issues would you consider out of scope for this proposal but can be addressed in the future? -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this will work with CPM enabled projects? See #12270

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeffkl
Could you be able to check if my changes affect any of CPM scenarios?


## Future Possibilities

<!-- What future possibilities can you think of that this proposal would help with? -->