Skip to content
Merged
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
15 changes: 13 additions & 2 deletions docs/design/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Do not set `Version`, `FileVersion` or `InformationalVersion` in `Bravo.csproj`:
| `AssemblyInformationalVersion` | `X.Y.Z.{height}[-tag]+{commit}` | diagnostics |
| `AssemblyVersion` | `X.Y.0.0` | assembly identity (`assemblyVersion.precision: minor`) |
| `NBGV_SimpleVersion` | `X.Y.Z` | WiX `-dVersion` |
| `NBGV_SemVer2` | `X.Y.Z[-tag]` | artifact names, git tag |
| `NBGV_SemVer2` | `X.Y.Z[-tag]` | artifact names, git tag, `AppVersion.SemanticVersion` |

`{height}` is the number of commits since the numeric `X.Y.Z` last changed. It is a build counter: it makes
every build uniquely identifiable and orders builds that share the same `X.Y.Z`.
Expand All @@ -33,6 +33,12 @@ every build uniquely identifiable and orders builds that share the same `X.Y.Z`.
- **The prerelease tag never reaches a numeric field.** `AssemblyFileVersion` and `NBGV_SimpleVersion` stay
numeric in every state, so Windows Installer and `System.Version` keep working unchanged.

- **`SemVer2` is the version that the application and the artifacts report.** The
application reads it as `ThisAssembly.NuGetPackageVersion`, which equals `SemVer2` only while
`nuGetPackageVersion.semVer` is `2` in `version.json` and `NBGV_ThisAssemblyIncludesPackageVersion` is set in
`Bravo.csproj`. Without the first, the application reports the SemVer1 form `X.Y.Z-tag-0001-g{commit}`; without
the second, it does not compile. A test in `AppVersionTests` checks the shape.

- **The height resets only when the numeric `X.Y.Z` changes.** Adding, changing or removing the prerelease tag
does not reset it. `AssemblyFileVersion` is therefore monotonic across `1.1.0-beta.1 → 1.1.0-beta.2 → 1.1.0`,
which is what makes a preview and its final release — identical on `X.Y.Z` — orderable.
Expand All @@ -43,12 +49,17 @@ to its final release changes `version.json` alone: such a filter would give the
`AssemblyFileVersion`.

- **`publicReleaseRefSpec` lists the branches that produce clean versions.** Outside them, `SemVer2` and
`NuGetPackageVersion` carry a `.g{commit}` suffix; numeric fields are unaffected. A tag checkout runs in
`NuGetPackageVersion` carry the commit id: `X.Y.Z-tag.g{commit}`, or `X.Y.Z-g{commit}` without a prerelease
tag; numeric fields and `AssemblyInformationalVersion` are unaffected. A tag checkout runs in
detached HEAD and matches no branch pattern, so building from a tag requires adding the tag pattern.

- **Artifact names come from `SemVer2`, not from `SimpleVersion`.** The two are identical for a release and
differ only for a preview, where `SimpleVersion` drops the tag: naming artifacts from it would give a preview
and the release that follows it the same file names.

- **NBGV reads `version.json` from `HEAD`, not from the working copy.** An edit takes effect from the commit
that contains it: a build or a test run on a dirty `version.json` still reflects the committed values, and only
the height resets.

- **The height needs full history.** Shallow clones make NBGV fail or compute a wrong number, so both
pipelines check out with unlimited depth.
1 change: 1 addition & 0 deletions src/Bravo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<SatelliteResourceLanguages>en-US</SatelliteResourceLanguages>
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<NBGV_ThisAssemblyIncludesPackageVersion>true</NBGV_ThisAssemblyIncludesPackageVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
Expand Down
53 changes: 12 additions & 41 deletions src/Infrastructure/AppVersion.cs
Original file line number Diff line number Diff line change
@@ -1,56 +1,27 @@
namespace Sqlbi.Bravo.Infrastructure;

/// <summary>
/// Application version, stamped from version.json by Nerdbank.GitVersioning.
/// Provides application version information generated by Nerdbank.GitVersioning from <c>version.json</c>.
/// </summary>
internal static class AppVersion
{
static AppVersion()
{
IsPrerelease = ThisAssembly.IsPrerelease;
IsPublicRelease = ThisAssembly.IsPublicRelease;
FileVersion = ThisAssembly.AssemblyFileVersion;
InformationalVersion = ThisAssembly.AssemblyInformationalVersion;
SemanticVersion = System.Version.Parse(FileVersion).ToString(3) + GetPrereleaseTag(InformationalVersion);
}

/// <summary>
/// True if the build is a prerelease, false if it is a release.
/// </summary>
public static bool IsPrerelease { get; }

/// <summary>
/// True if the build is a public release, false if it is a internal build (e.g. CI build).
/// </summary>
public static bool IsPublicRelease { get; }

/// <summary>
/// Four-part assembly file version <c>Major.Minor.Patch.Height</c>, where <c>Height</c>
/// is the version height used to distinguish builds of the same release.
/// Four-part file version in the form <c>Major.Minor.Patch.Height</c>, where <c>Height</c>
/// distinguishes builds of the same version.
/// </summary>
public static string FileVersion { get; }
public static string FileVersion { get; } = ThisAssembly.AssemblyFileVersion;

/// <summary>
/// Semantic version of the application, including the prerelease label when present
/// and excluding build metadata. e.g. <c>1.1.0-beta.1</c> or <c>1.1.0</c>.
/// Semantic version of the application, including the prerelease label when present and
/// excluding build metadata, for example <c>1.1.0-beta.1</c> or <c>1.1.0</c>.
/// Internal builds include the Git commit identifier, for example
/// <c>1.1.0-beta.1.g1c52e441d1</c>, or <c>1.1.0-g1c52e441d1</c> without a prerelease label.
/// </summary>
public static string SemanticVersion { get; }
public static string SemanticVersion { get; } = ThisAssembly.NuGetPackageVersion;

/// <summary>
/// <see cref="FileVersion"/> with the prerelease tag, if any, and the git commit id:
/// <c>1.1.0.14-beta.1+1c52e441d1</c>.
/// Informational version of the application, including the file version, prerelease label
/// when present, and Git commit identifier, for example <c>1.1.0.14-beta.1+1c52e441d1</c>.
/// </summary>
public static string InformationalVersion { get; }

internal static string GetPrereleaseTag(string informationalVersion)
{
var value = informationalVersion;

var metadataIndex = value.IndexOf('+');
if (metadataIndex >= 0)
value = value[..metadataIndex];

var prereleaseIndex = value.IndexOf('-');
return prereleaseIndex < 0 ? string.Empty : value[prereleaseIndex..];
}
public static string InformationalVersion { get; } = ThisAssembly.AssemblyInformationalVersion;
}
1 change: 0 additions & 1 deletion src/Infrastructure/AppWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,6 @@ private MemoryStream GetConfigJs()
address = _serverAddressProvider.GetListeningAddress(),
token = AppEnvironment.ApiAuthenticationToken,
version = AppVersion.SemanticVersion,
informationalVersion = AppVersion.InformationalVersion,
options = BravoOptions.CreateFromUserPreferences(),
policies = _policies,
culture = new
Expand Down
9 changes: 1 addition & 8 deletions src/Infrastructure/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@ internal static class StringExtensions

public static string AppendApplicationVersion(this string value)
{
var result = $"{value} - v{AppVersion.SemanticVersion}";

if (AppVersion.IsPrerelease || !AppVersion.IsPublicRelease)
{
result += $" ({AppVersion.InformationalVersion})";
}

return result;
return $"{value} - v{AppVersion.SemanticVersion}";
}

/// <summary>
Expand Down
1 change: 0 additions & 1 deletion src/Scripts/@types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ declare global {
debug?: boolean,
address: string
version: string,
informationalVersion: string,
options: Options,
policies?: Policies,
token?: string,
Expand Down
1 change: 0 additions & 1 deletion src/Scripts/controllers/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import { DialogResponse } from '../view/dialog';

export interface AppVersionInfo {
version: string
informationalVersion?: string
downloadUrl?: string
changelogUrl?: string
}
Expand Down
1 change: 0 additions & 1 deletion src/Scripts/controllers/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export class Debug {
debug: true,
address: "http://localhost",
version: "0.0.0-debug",
informationalVersion: "0.0.0.0-debug+0000000000",
options: null,
token: "",
culture: {
Expand Down
3 changes: 1 addition & 2 deletions src/Scripts/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ let pbiDesktop = new PBIDesktop();
let notificationCenter = new NotifyCenter();

let app = new App(new AppVersion({
version: CONFIG.version,
informationalVersion: CONFIG.informationalVersion
version: CONFIG.version
}));

export { debug, host, optionsController, themeController, auth, telemetry, pbiDesktop, notificationCenter, logger, app };
2 changes: 1 addition & 1 deletion src/Scripts/view/options-dialog-about.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class OptionsDialogAbout {

_(".copy-version", element).addEventListener("click", e => {
e.preventDefault();
navigator.clipboard.writeText(app.currentVersion.info.informationalVersion ?? app.currentVersion.toString());
navigator.clipboard.writeText(app.currentVersion.toString());
});

_(".auto-check-option input", element).addEventListener("change", e => {
Expand Down
43 changes: 30 additions & 13 deletions test/Bravo.Tests/Infrastructure/AppVersionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,6 @@ namespace Bravo.Tests.Infrastructure;

public class AppVersionTests
{
[Theory]
[InlineData("1.1.0.11+ed89094be9", "")]
[InlineData("1.1.0.13-beta.1+57e453666e", "-beta.1")]
[InlineData("1.1.0.13-beta.1", "-beta.1")]
[InlineData("1.1.0.0-build.99+74e5ed577e", "-build.99")]
[InlineData("1.1.0.13", "")]
public void GetPrereleaseTag_ReadsTheTag(string informationalVersion, string expected)
{
var actual = AppVersion.GetPrereleaseTag(informationalVersion);

Assert.Equal(expected, actual);
}

[Fact]
public void FileVersion_IsNumericWithFourFields()
{
Expand All @@ -37,6 +24,36 @@ public void SemanticVersion_StartsWithTheNumericReleaseVersion()
Assert.StartsWith(releaseVersion, AppVersion.SemanticVersion);
}

[Fact]
public void SemanticVersion_HasNoBuildMetadata()
{
Assert.DoesNotContain("+", AppVersion.SemanticVersion);
}

[Fact]
public void SemanticVersion_CarriesTheCommitIdOnlyOutsideAPublicRelease()
{
// SemanticVersion must be NBGV SemVer2, the value used for artifact names and installer telemetry: outside
// publicReleaseRefSpec it ends with 'g{commit}', where the commit is the build metadata of the informational
// version.
var metadataIndex = AppVersion.InformationalVersion.IndexOf('+');
Assert.True(metadataIndex >= 0, $"'{AppVersion.InformationalVersion}' has no build metadata.");

var commitId = AppVersion.InformationalVersion[(metadataIndex + 1)..];
var isPublicRelease = ThisAssembly.IsPublicRelease;
var isPrerelease = ThisAssembly.IsPrerelease;

if (isPublicRelease)
{
Assert.DoesNotContain(commitId, AppVersion.SemanticVersion);
}
else
{
var separator = isPrerelease ? '.' : '-';
Assert.EndsWith($"{separator}g{commitId}", AppVersion.SemanticVersion);
}
}

[Fact]
public void InformationalVersion_StartsWithFileVersion()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ public void AppendApplicationVersion_Test()
{
var actual = "Bravo for Power BI".AppendApplicationVersion();

Assert.NotNull(actual);
Assert.StartsWith("Bravo for Power BI", actual);
Assert.Equal($"Bravo for Power BI - v{AppVersion.SemanticVersion}", actual);
}

[Fact]
Expand Down
4 changes: 4 additions & 0 deletions version.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"assemblyVersion": {
"precision": "minor"
},
"nuGetPackageVersion": {
"semVer": 2,
"precision": "build"
},
"pathFilters": [
":/"
],
Expand Down
Loading