Skip to content

Commit 2cb734c

Browse files
authored
#19 Fix publish command to compare the last two commits (HEAD~1 and HEAD) (#20)
1 parent 993fd6b commit 2cb734c

15 files changed

Lines changed: 292 additions & 47 deletions

docs/commands-implementation-details.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,36 @@ If there are any existing changesets the command generates changelogs for every
5353
changeset publish
5454
```
5555

56-
Creates nuget packages of affected projects and publishes them to the predefined package source. The command:
56+
Creates NuGet packages of affected projects and publishes them to the predefined package source.
5757

58-
1. Gets all modified `.csproj` files from the predefined working directory using `git diff --name-only`.
59-
1. Creates nuget package using `nuget pack` for every csproj file.
60-
1. Pushes the created nuget packages to the predefined nuget source using `nuget push`.
58+
### How it works (current .NET implementation)
6159

62-
The package source can be configured via `config.json` file.
60+
Currently, the `publish` command assumes that the changes made by the `version` command have **already been committed**. The intended flow is:
61+
62+
1. Run `changeset version` to bump versions, update changelogs, and delete processed changesets.
63+
2. Commit the changes produced by the `version` command.
64+
3. Run `changeset publish` to create and push NuGet packages based on the committed changes.
65+
66+
The `publish` command does following:
67+
68+
1. Gets all modified `.csproj` files from the predefined source directory by comparing the last two commits `git diff --name-only HEAD~1 HEAD {sourcePath}`
69+
2. For each changed `.csproj` file, creates a NuGet package using `dotnet pack`.
70+
3. Pushes the created NuGet packages to the predefined NuGet source using `dotnet nuget push`.
71+
72+
The package source can be configured via the `.changeset/config.json` file (see `docs/config-file-options.md`).
73+
74+
This approach relies on the fact that the `version` command only performs three types of changes:
75+
76+
- Deleting processed changeset files from the `.changeset` folder
77+
- Modifying or creating `CHANGELOG.md` files
78+
- Bumping versions in `.csproj` files
79+
80+
By looking at the diff between `HEAD~1` and `HEAD`, `publish` can safely identify which projects had their versions bumped and therefore need to be published.
81+
82+
### Comparison with original Node.js changesets implementation
83+
84+
The original `@changesets/cli` implementation for Node.js works differently. Instead of relying on a Git diff,
85+
it checks whether a package with the **current version** from `package.json` already exists in the package registry; if it does not, the package is published.
6386

6487
## `status`
6588

src/SolarWinds.Changesets/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# SolarWinds.Changesets
22

3+
## 0.1.4
4+
5+
**Patch Changes**:
6+
7+
- [[#19](https://github.com/solarwinds/net-changesets/issues/19)] Fix publish command ([PR #14](https://github.com/solarwinds/net-changesets/pull/14))
8+
- The `publish` command now compares the last two commits (HEAD~1 and HEAD) instead of comparing the last commit with the working tree to determine which projects were published.
9+
This change is required because publishing must be done after version bumps and generated files are committed. Since there is no other reliable way to detect whether the version command was run before publish,
10+
the comparison is based on committed changes in `.csproj` files. This is still error-prone if users make manual changes to `.csproj` files and run `publish` command, but it is a reasonable compromise for now.
11+
312
## 0.1.3
413

514
**Patch Changes**:

src/SolarWinds.Changesets/Commands/Publish/PublishChangesetCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using SolarWinds.Changesets.Services;
1+
using SolarWinds.Changesets.Commands.Publish.Services;
22
using SolarWinds.Changesets.Shared;
33
using Spectre.Console;
44
using Spectre.Console.Cli;
@@ -40,7 +40,7 @@ public override async Task<int> ExecuteCommandAsync(CommandContext context)
4040
_console.WriteLine("Determining projects to publish ...");
4141
_console.WriteLine();
4242

43-
ProcessOutput processOutput = await _gitService.GetDiff(ChangesetConfig.SourcePath);
43+
ProcessOutput processOutput = await _gitService.GetDiff(Constants.WorkingDirectoryFullPath, ChangesetConfig.SourcePath);
4444
List<string> changedCsharpProjectNames = processOutput.Output.Where(x => x.Contains(".csproj", StringComparison.Ordinal)).ToList();
4545

4646
if (changedCsharpProjectNames.Count == 0)

src/SolarWinds.Changesets/Services/DotnetService.cs renamed to src/SolarWinds.Changesets/Commands/Publish/Services/DotnetService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using SolarWinds.Changesets.Shared;
22

3-
namespace SolarWinds.Changesets.Services;
3+
namespace SolarWinds.Changesets.Commands.Publish.Services;
44

55
/// <inheritdoc />
66
public sealed class DotnetService : IDotnetService

src/SolarWinds.Changesets/Services/GitService.cs renamed to src/SolarWinds.Changesets/Commands/Publish/Services/GitService.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using SolarWinds.Changesets.Shared;
22

3-
namespace SolarWinds.Changesets.Services;
3+
namespace SolarWinds.Changesets.Commands.Publish.Services;
44

55
/// <inheritdoc />
66
public sealed class GitService : IGitService
@@ -19,12 +19,12 @@ public GitService(IProcessExecutor processExecutor)
1919
}
2020

2121
/// <inheritdoc />
22-
public async Task<ProcessOutput> GetDiff(string sourcePath)
22+
public async Task<ProcessOutput> GetDiff(string workingDirectory, string sourcePath)
2323
{
2424
return await _processExecutor.Execute(
2525
"git",
26-
$"diff --name-only {sourcePath}",
27-
Constants.WorkingDirectoryFullPath
26+
$"diff --name-only HEAD~1 HEAD {sourcePath}",
27+
workingDirectory
2828
);
2929
}
3030
}

src/SolarWinds.Changesets/Services/IDotnetService.cs renamed to src/SolarWinds.Changesets/Commands/Publish/Services/IDotnetService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using SolarWinds.Changesets.Shared;
22

3-
namespace SolarWinds.Changesets.Services;
3+
namespace SolarWinds.Changesets.Commands.Publish.Services;
44

55
/// <summary>
66
/// Provides functionality to interact with the .NET CLI for operations such as packing and publishing NuGet packages.

src/SolarWinds.Changesets/Services/IGitService.cs renamed to src/SolarWinds.Changesets/Commands/Publish/Services/IGitService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using SolarWinds.Changesets.Shared;
22

3-
namespace SolarWinds.Changesets.Services;
3+
namespace SolarWinds.Changesets.Commands.Publish.Services;
44

55
/// <summary>
66
/// Provides Git-related services, such as retrieving file differences.
@@ -10,7 +10,8 @@ public interface IGitService
1010
/// <summary>
1111
/// Retrieves a list of file names that have changed in the specified source path.
1212
/// </summary>
13+
/// <param name="workingDirectory">Working directory.</param>
1314
/// <param name="sourcePath">The path to check for file differences.</param>
1415
/// <returns>Process output and exit code.</returns>
15-
Task<ProcessOutput> GetDiff(string sourcePath);
16+
Task<ProcessOutput> GetDiff(string workingDirectory, string sourcePath);
1617
}

src/SolarWinds.Changesets/Commands/Version/Helpers/CsProjectsRepository.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,9 @@ public CsProject[] GetCsProjects(ChangesetConfig changesetConfig)
2929
XDocument csprojXDocument = XDocument.Load(csprojFilePath);
3030
string projectName = Path.GetFileNameWithoutExtension(csprojFilePath);
3131

32-
XElement? projectVersionXElement = csprojXDocument.Descendants().SingleOrDefault(d => d.Name.LocalName == "Version");
33-
Semver? projectVersion = projectVersionXElement is not null
34-
? Semver.FromString(projectVersionXElement.Value)
35-
: new(0, 0, 0);
36-
37-
if (projectVersion is null)
32+
Semver projectVersion = GetProjectVersion(csprojXDocument, projectName);
33+
if (projectVersion == Semver.Empty)
3834
{
39-
_console.MarkupLine($"[yellow]Version {projectVersionXElement?.Value} could not be parsed " +
40-
$"for project {projectName}. This may have unexpected consequences on the 'version' command![/]");
4135
continue;
4236
}
4337

@@ -49,7 +43,6 @@ public CsProject[] GetCsProjects(ChangesetConfig changesetConfig)
4943
.ToArray();
5044

5145
csProjects.Add(new(projectName, projectVersion, projectReferences, csprojFilePath));
52-
5346
}
5447

5548
return csProjects.ToArray();
@@ -85,6 +78,21 @@ private async Task UpdateCsProjectVersionAsync(string moduleCsProjFilePath, stri
8578
await File.WriteAllTextAsync(moduleCsProjFilePath, newVersionContent);
8679
}
8780

81+
private Semver GetProjectVersion(XDocument csprojXDocument, string projectName)
82+
{
83+
XElement? projectVersionXElement = csprojXDocument.Descendants().SingleOrDefault(d => d.Name.LocalName == "Version");
84+
85+
if (projectVersionXElement is not null && Semver.TryParse(projectVersionXElement.Value, out Semver? parsedVersion))
86+
{
87+
return parsedVersion;
88+
}
89+
90+
_console.MarkupLine($"[yellow]Version {projectVersionXElement?.Value} could not be parsed " +
91+
$"for project {projectName}. This may have unexpected consequences on the 'version' command![/]");
92+
93+
return Semver.Empty;
94+
}
95+
8896
[GeneratedRegex(@"(<Version>)(.*?)(</Version>)")]
8997
private static partial Regex VersionRegex();
9098
}

src/SolarWinds.Changesets/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
using SolarWinds.Changesets.Commands.Add;
33
using SolarWinds.Changesets.Commands.Init;
44
using SolarWinds.Changesets.Commands.Publish;
5+
using SolarWinds.Changesets.Commands.Publish.Services;
56
using SolarWinds.Changesets.Commands.Status;
67
using SolarWinds.Changesets.Commands.Version;
78
using SolarWinds.Changesets.Commands.Version.Helpers;
89
using SolarWinds.Changesets.Infrastructure;
9-
using SolarWinds.Changesets.Services;
1010
using SolarWinds.Changesets.Shared;
1111
using Spectre.Console.Cli;
1212

src/SolarWinds.Changesets/Shared/Semver.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics.CodeAnalysis;
12
using SystemVersion = System.Version;
23

34
namespace SolarWinds.Changesets.Shared;
@@ -45,13 +46,27 @@ public override string ToString()
4546
return $"{Major}.{Minor}.{Patch}";
4647
}
4748

48-
public static Semver? FromString(string version)
49+
/// <summary>
50+
/// Attempts to parse a version string into a <see cref="Semver"/> instance.
51+
/// </summary>
52+
/// <param name="version">The version string to parse (e.g., "1.2.3").</param>
53+
/// <param name="semver">When this method returns, contains the parsed <see cref="Semver"/> if parsing succeeded, or null if parsing failed.</param>
54+
/// <returns>True if the version string was successfully parsed; otherwise, false.</returns>
55+
public static bool TryParse(string version, [NotNullWhen(true)] out Semver? semver)
4956
{
50-
if (!SystemVersion.TryParse(version, out SystemVersion? result))
57+
if (SystemVersion.TryParse(version, out SystemVersion? result))
5158
{
52-
return null;
59+
semver = new(result.Major, result.Minor, result.Build);
60+
return true;
5361
}
5462

55-
return new(result.Major, result.Minor, result.Build);
63+
semver = null;
64+
return false;
5665
}
66+
67+
/// <summary>
68+
/// Gets an empty <see cref="Semver"/> instance with version 0.0.0.
69+
/// </summary>
70+
/// <value>A <see cref="Semver"/> instance representing version 0.0.0.</value>
71+
public static Semver Empty { get; } = new(0, 0, 0);
5772
}

0 commit comments

Comments
 (0)