Skip to content

Commit efb73ba

Browse files
committed
temp [rebase]
1 parent 975ff57 commit efb73ba

File tree

7 files changed

+502
-16
lines changed

7 files changed

+502
-16
lines changed

.github/workflows/main.yml

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,27 @@ name: 'Build'
22
'on':
33
push:
44
branches:
5-
- 'develop'
65
- 'master'
76

87
jobs:
98
build:
109

1110
runs-on: 'windows-latest'
1211

13-
env:
14-
RepositoryUrl: 'https://github.com/${{ github.repository }}'
15-
RepositoryBranch: '${{ github.ref }}'
16-
SourceRevisionId: '${{ github.sha }}'
17-
1812
steps:
19-
- name: 'Checkout repository'
20-
uses: 'actions/checkout@v2'
13+
- name: 'Checkout'
14+
uses: 'actions/checkout@v6'
2115

2216

2317
- name: 'Setup .NET'
24-
uses: 'actions/setup-dotnet@v1'
18+
uses: 'actions/setup-dotnet@v5'
2519
with:
26-
dotnet-version: 5.0.x
27-
28-
29-
- name: 'Setup MSBuild'
30-
uses: 'microsoft/[email protected]'
31-
20+
dotnet-version: 10.0.x
21+
3222

3323
- name: 'Build'
34-
run : 'msbuild NuGetSwitcher.sln /t:Restore /t:Rebuild -property:Configuration=Release'
24+
run : 'dotnet build -c Release'
25+
26+
27+
- name: 'Pack'
28+
uses: 'dotnet pack -p:PackAsTool=true -p:ToolCommandName=nswitch -p:PackageOutputPath=../nupkg'
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using CliWrap;
2+
using CliWrap.Buffered;
3+
using CliWrap.Builders;
4+
5+
using NuGet.ProjectModel;
6+
7+
using NuGetSwitcher.Interface.Logger;
8+
using NuGetSwitcher.Interface.Logger.Enums;
9+
10+
using NuGetSwitcher.Interface.Options;
11+
using NuGetSwitcher.Interface.References;
12+
13+
using System;
14+
using System.Collections.Generic;
15+
16+
using System.IO;
17+
using System.Linq;
18+
19+
namespace NuGetSwitcher.Core.Commands
20+
{
21+
public abstract class AbstractCommand(string referenceType, IOptions option, ILogger logger)
22+
{
23+
public abstract void Switch();
24+
25+
/// <summary>
26+
/// Iterates through the dependencies provided in the lock file and matches
27+
/// against items found in the directories listed in the configuration file.
28+
/// For each matched item, the passed delegate is executed.
29+
/// </summary>
30+
protected virtual void IterateAndExecute<TReference>(IEnumerable<TReference> references, Action<IProjectReference, LockFileTargetLibrary, string> func) where TReference : IProjectReference
31+
{
32+
logger.Clear();
33+
34+
IReadOnlyDictionary<string, string> items = option.GetIncludeItems(referenceType);
35+
36+
foreach (IProjectReference reference in references)
37+
{
38+
foreach (LockFileTargetLibrary library in reference.GetLockFileLibraries())
39+
{
40+
if (items.TryGetValue(library.Name, out string absolutePath))
41+
{
42+
func(reference, library, absolutePath);
43+
}
44+
}
45+
46+
reference.Save();
47+
}
48+
}
49+
50+
/// <summary>
51+
/// Adds reference to the project. It is assumed
52+
/// that the original reference has been removed
53+
/// earlier.
54+
/// </summary>
55+
///
56+
/// <param name="unevaluatedInclude">
57+
/// Must contain the assembly
58+
/// name or the absolute path
59+
/// to the project or Package
60+
/// Id.
61+
/// </param>
62+
protected virtual void AddReference(IProjectReference reference, string referenceType, string unevaluatedInclude, Dictionary<string, string> metadata)
63+
{
64+
if (reference.TryAddReference(unevaluatedInclude, referenceType, metadata))
65+
{
66+
logger.LogMessage($"{referenceType}: {Path.GetFileName(unevaluatedInclude)} has been added", Category.I, project: reference.Project.FullPath);
67+
}
68+
}
69+
70+
/// <summary>
71+
/// Performs an action on the solution file, taking
72+
/// into account the passed <paramref name="builder"/>.
73+
/// </summary>
74+
protected virtual void SlnAction(string solution, IEnumerable<string> projects, ArgumentsBuilder builder)
75+
{
76+
void Log(string source, Category category)
77+
{
78+
if (!string.IsNullOrWhiteSpace(source))
79+
{
80+
logger.Clear();
81+
logger.LogMessage(source, category);
82+
}
83+
}
84+
85+
if (projects.Any())
86+
{
87+
// TODO: Switch to async.
88+
BufferedCommandResult result = Cli.Wrap("dotnet").WithWorkingDirectory(Path.GetDirectoryName(solution)).WithArguments(args => args.Add("sln").Add(solution).Add(builder.Build(), false)).ExecuteBufferedAsync().GetAwaiter().GetResult();
89+
90+
Log(result.StandardOutput, Category.I);
91+
Log(result.StandardError, Category.E);
92+
}
93+
}
94+
}
95+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using CliWrap.Builders;
2+
3+
using Microsoft.Build.Evaluation;
4+
5+
using NuGetSwitcher.Core.Constants;
6+
using NuGetSwitcher.Core.References;
7+
using NuGetSwitcher.Interface.Logger;
8+
using NuGetSwitcher.Interface.Logger.Enums;
9+
10+
using NuGetSwitcher.Interface.Options;
11+
using NuGetSwitcher.Interface.References;
12+
using NuGetSwitcher.Interface.Solution;
13+
14+
using System.Collections.Generic;
15+
using System.IO;
16+
using System.Linq;
17+
18+
namespace NuGetSwitcher.Core.Commands
19+
{
20+
public sealed class PackageCommand(IOptions option, ISolution solution, ILogger logger) : AbstractCommand(ReferenceType.PackageReference, option, logger)
21+
{
22+
/// <summary>
23+
/// Returns references that match the passed type and are marked with the Temp attribute.
24+
/// </summary>
25+
private static IEnumerable<ProjectItem> GetTempItems(IProjectReference reference, string referenceType)
26+
{
27+
return reference.Project.GetItems(referenceType).Where(i => i.HasMetadata("Temp")).ToList();
28+
}
29+
30+
/// <summary>
31+
/// Removes projects from the solution file.
32+
/// </summary>
33+
private void RemoveFromSolution(string absolutePath, IEnumerable<string> projects)
34+
{
35+
SlnAction(absolutePath, projects, new ArgumentsBuilder().Add("remove").Add(projects));
36+
}
37+
38+
/// <summary>
39+
/// Removes references
40+
/// that marked with the Temp attribute and originated from
41+
/// the Dependencies or FrameworkAssemblies sections of the
42+
/// lock file.
43+
/// </summary>
44+
public IEnumerable<string> SwitchDependencies(IProjectReference reference, string referenceType)
45+
{
46+
List<string> projects = new
47+
List<string>
48+
();
49+
50+
foreach (ProjectItem item in GetTempItems(reference, referenceType))
51+
{
52+
if (referenceType == ReferenceType.ProjectReference)
53+
{
54+
projects.Add(item.EvaluatedInclude);
55+
}
56+
57+
// Implicit.
58+
if (!item.HasMetadata("Version"))
59+
{
60+
reference.Project.RemoveItem(item);
61+
}
62+
// Explicit.
63+
else
64+
{
65+
item.UnevaluatedInclude = item.GetMetadataValue("Temp");
66+
67+
item.RemoveMetadata("Temp");
68+
item.RemoveMetadata("Name");
69+
70+
item.ItemType = referenceType;
71+
}
72+
73+
logger.LogMessage($"{referenceType}: {Path.GetFileName(item.EvaluatedInclude)} has been switched back", Category.I, project: reference.Project.FullPath);
74+
}
75+
76+
return projects;
77+
}
78+
79+
/// <inheritdoc/>
80+
public override void Switch()
81+
{
82+
HashSet<string> projects = new
83+
HashSet<string>
84+
();
85+
86+
foreach (IProjectReference reference in solution.GetLoadedProjects<ProjectReference>())
87+
{
88+
projects.UnionWith(SwitchDependencies(reference, ReferenceType.Reference));
89+
projects.UnionWith(SwitchDependencies(reference, ReferenceType.ProjectReference));
90+
91+
reference.Save();
92+
}
93+
94+
RemoveFromSolution(solution.AbsolutePath, projects);
95+
}
96+
}
97+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using CliWrap.Builders;
2+
3+
using Microsoft.Build.Evaluation;
4+
5+
using NuGet.ProjectModel;
6+
7+
using NuGetSwitcher.Core.Constants;
8+
using NuGetSwitcher.Core.References;
9+
using NuGetSwitcher.Interface.Logger;
10+
using NuGetSwitcher.Interface.Logger.Enums;
11+
12+
using NuGetSwitcher.Interface.Options;
13+
using NuGetSwitcher.Interface.References;
14+
using NuGetSwitcher.Interface.Solution;
15+
16+
using System.Collections.Generic;
17+
18+
namespace NuGetSwitcher.Core.Commands
19+
{
20+
public class ProjectCommand(IOptions option, ISolution solution, ILogger logger) : AbstractCommand(ReferenceType.ProjectReference, option, logger)
21+
{
22+
/// <summary>
23+
/// Adds one or more projects to the solution file.
24+
/// </summary>
25+
private void AddToSolution(string absolutePath, IEnumerable<string> projects)
26+
{
27+
base.SlnAction(absolutePath, projects, new ArgumentsBuilder().Add("add").Add(projects).Add("--solution-folder").Add("Temporary"));
28+
}
29+
30+
/// <summary>
31+
/// Includes references to the GAC assemblies
32+
/// listed in the FrameworkAssemblies section
33+
/// of the lock file.
34+
/// </summary>
35+
public virtual void SwitchSysDependency(IProjectReference reference, LockFileTargetLibrary library)
36+
{
37+
Dictionary<string, string> metadata = new
38+
Dictionary<string, string>
39+
{
40+
{ "Temp", library.Name }
41+
};
42+
43+
foreach (string assembly in library.FrameworkAssemblies)
44+
{
45+
base.AddReference(reference, ReferenceType.Reference, assembly, metadata);
46+
}
47+
}
48+
49+
/// <summary>
50+
/// Includes implicit, explicit project references
51+
/// listed in the Dependencies section of the lock
52+
/// file.
53+
/// </summary>
54+
///
55+
/// <remarks>
56+
/// Implicit dependencies mean transitive.
57+
/// </remarks>
58+
public virtual void SwitchPkgDependency(IProjectReference reference, LockFileTargetLibrary library, string absolutePath)
59+
{
60+
/*
61+
* References can be represented by several values in
62+
* an ItemGroup, for example, when included using the
63+
* Condition attribute.
64+
*/
65+
66+
ICollection<ProjectItem> items = reference.Project.GetItemsByEvaluatedInclude(library.Name);
67+
68+
// Implicit.
69+
if (items.Count == 0)
70+
{
71+
base.AddReference(reference, ReferenceType.ProjectReference, absolutePath, new Dictionary<string, string>(2)
72+
{
73+
{ "Temp", library.Name }
74+
});
75+
}
76+
// Explicit.
77+
else
78+
{
79+
/*
80+
* Re-creating an item can lead to the loss
81+
* of user metadata; in order to avoid this,
82+
* the item is redefined.
83+
*/
84+
85+
foreach (ProjectItem item in items)
86+
{
87+
item.ItemType = ReferenceType.ProjectReference;
88+
89+
item.SetMetadataValue("Temp", item.EvaluatedInclude);
90+
item.SetMetadataValue("Name", item.EvaluatedInclude);
91+
92+
item.UnevaluatedInclude = absolutePath;
93+
}
94+
95+
logger.LogMessage($"Dependency: {library.Name} has been switched. Type: {ReferenceType.ProjectReference}", Category.I, project: reference.Project.FullPath);
96+
}
97+
}
98+
99+
/// <inheritdoc/>
100+
public override void Switch()
101+
{
102+
HashSet<string> packages = new
103+
HashSet<string>
104+
();
105+
106+
void Executor(IProjectReference reference, LockFileTargetLibrary library, string absolutePath)
107+
{
108+
SwitchSysDependency(reference, library);
109+
SwitchPkgDependency(reference, library, absolutePath);
110+
111+
packages.Add(absolutePath);
112+
}
113+
114+
IterateAndExecute(solution.GetLoadedProjects<ProjectReference>(), Executor);
115+
116+
AddToSolution(solution.AbsolutePath, packages);
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)