Skip to content

Adding perpetual licensing#4608

Merged
jbogard merged 7 commits intomainfrom
perpetual
Feb 24, 2026
Merged

Adding perpetual licensing#4608
jbogard merged 7 commits intomainfrom
perpetual

Conversation

@jbogard
Copy link
Contributor

@jbogard jbogard commented Feb 23, 2026

No description provided.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds perpetual licensing support to AutoMapper, allowing licenses to remain valid for builds created before the license expiration date, even after the license has expired. The implementation introduces a build date timestamp that is embedded in the assembly at compile time and checked against the license expiration date during validation.

Changes:

  • Added build date tracking via assembly metadata embedded during compilation
  • Implemented perpetual license claim parsing and validation logic
  • Added comprehensive test coverage for perpetual licensing scenarios

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/AutoMapper/Licensing/BuildInfo.cs New class to extract build date from assembly metadata attributes
src/AutoMapper/AutoMapper.csproj MSBuild target to embed current UTC timestamp as assembly metadata during compilation
src/AutoMapper/Licensing/License.cs Added IsPerpetual property to parse "perpetual" claim from license tokens
src/AutoMapper/Licensing/LicenseValidator.cs Enhanced validation logic to allow expired perpetual licenses when build date precedes expiration
src/UnitTests/Licensing/LicenseValidatorTests.cs Three new test cases covering perpetual license validation scenarios

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +64 to +72
<Target Name="EmbedBuildDate" BeforeTargets="CoreCompile">
<PropertyGroup>
<BuildDateUtc>$([System.DateTime]::UtcNow.ToString("O"))</BuildDateUtc>
</PropertyGroup>
<WriteLinesToFile File="$(IntermediateOutputPath)BuildDateGenerated.cs" Lines="[assembly: System.Reflection.AssemblyMetadata(&quot;BuildDateUtc&quot;, &quot;$(BuildDateUtc)&quot;)]" Overwrite="true" />
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)BuildDateGenerated.cs" />
</ItemGroup>
</Target>
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The EmbedBuildDate target uses DateTime.UtcNow which makes every build produce a different assembly, conflicting with the Deterministic build setting on line 26. This will break deterministic builds and may cause issues with build caching, reproducible builds, and package signing.

Consider using a source control-based approach (like git commit timestamp) or an environment variable that can be set to a fixed value in CI/CD pipelines while allowing dynamic values for local development builds. For example, you could check for an environment variable first: BuildDateUtc>$([System.Environment]::GetEnvironmentVariable('BUILD_DATE_UTC', 'EnvironmentVariableTarget.Process'))</BuildDateUtc> and only fall back to UtcNow if not set, or use SOURCE_DATE_EPOCH for reproducible builds.

Copilot uses AI. Check for mistakes.
Comment on lines +173 to +199
[Fact]
public void Should_handle_missing_perpetual_claim()
{
var factory = new LoggerFactory();
var provider = new FakeLoggerProvider();
factory.AddProvider(provider);

var licenseValidator = new LicenseValidator(factory);
var license = new License(
new Claim("account_id", Guid.NewGuid().ToString()),
new Claim("customer_id", Guid.NewGuid().ToString()),
new Claim("sub_id", Guid.NewGuid().ToString()),
new Claim("iat", DateTimeOffset.UtcNow.AddDays(-1).ToUnixTimeSeconds().ToString()),
new Claim("exp", DateTimeOffset.UtcNow.AddDays(1).ToUnixTimeSeconds().ToString()),
new Claim("edition", nameof(Edition.Community)),
new Claim("type", nameof(AutoMapper.Licensing.ProductType.Bundle)));

license.IsConfigured.ShouldBeTrue();
license.IsPerpetual.ShouldBeFalse();

licenseValidator.Validate(license);

var logMessages = provider.Collector.GetSnapshot();
logMessages.ShouldNotContain(log => log.Level == LogLevel.Error
|| log.Level == LogLevel.Warning
|| log.Level == LogLevel.Critical);
}
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The tests don't cover the edge case where a perpetual license is used but the build date is unavailable (null). According to the validation logic on line 37 of LicenseValidator.cs, if _buildDate is null, the perpetual license feature won't activate and the expired license will be rejected.

Add a test case that validates an expired perpetual license when no build date is provided (call new LicenseValidator(factory) without passing a buildDate, and ensure BuildInfo.BuildDate returns null in the test context). This should verify that the system gracefully handles this scenario and logs an appropriate error.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

jbogard and others added 3 commits February 24, 2026 10:41
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

This was referenced Mar 4, 2026
PhilipWoulfe pushed a commit to PhilipWoulfe/F1Competition that referenced this pull request Mar 10, 2026
Updated [AutoMapper](https://github.com/LuckyPennySoftware/AutoMapper)
from 14.0.0 to 16.1.0.

<details>
<summary>Release notes</summary>

_Sourced from [AutoMapper's
releases](https://github.com/LuckyPennySoftware/AutoMapper/releases)._

## 16.1.0

## What's Changed
* Add Debug and Release build configurations to slnx by @​Copilot in
LuckyPennySoftware/AutoMapper#4590
* Migrating to slnx by @​jbogard in
LuckyPennySoftware/AutoMapper#4589
* Allow disabling of polymorphic LINQ mapping by @​jbogard in
LuckyPennySoftware/AutoMapper#4596
* Fix duplicate BOM in ServiceCollectionExtensions.cs by @​Copilot in
LuckyPennySoftware/AutoMapper#4600
* Fix review feedback: double semicolon, DI condition integration test,
docs example by @​Copilot in
LuckyPennySoftware/AutoMapper#4601
* Adding DI-enabled conditions and pre-conditions; updated docs accordi…
by @​jbogard in
LuckyPennySoftware/AutoMapper#4599
* Adding support for DI-enabled destination factories. by @​jbogard in
LuckyPennySoftware/AutoMapper#4603
* Correctly converting nullables for MapAtRuntime; fixes #​4597 by
@​jbogard in LuckyPennySoftware/AutoMapper#4604
* Correctly handling consecutive uppercase characters; fixes #​4593 by
@​jbogard in LuckyPennySoftware/AutoMapper#4605
* Wrapping the exception to provide better feedback to the user; fixes …
by @​jbogard in
LuckyPennySoftware/AutoMapper#4606
* Fixing bug around order of open generic registration by @​jbogard in
LuckyPennySoftware/AutoMapper#4607
* Adding perpetual licensing by @​jbogard in
LuckyPennySoftware/AutoMapper#4608

## New Contributors
* @​Copilot made their first contribution in
LuckyPennySoftware/AutoMapper#4590

**Full Changelog**:
LuckyPennySoftware/AutoMapper@v16.0.0...v16.1.0

## 16.0.0

## What's Changed
* Fix release pipelines by @​jbogard in
LuckyPennySoftware/AutoMapper#4583
* Adding support for .NET 10 by @​jbogard in
LuckyPennySoftware/AutoMapper#4586


**Full Changelog**:
LuckyPennySoftware/AutoMapper@v15.1.0...v16.0.0

## 16.0.0-beta-1

## What's Changed
* Fix release pipelines by @​jbogard in
LuckyPennySoftware/AutoMapper#4583
* Adding support for .NET 10 by @​jbogard in
LuckyPennySoftware/AutoMapper#4586

**Full Changelog**:
LuckyPennySoftware/AutoMapper@v15.1.0...v16.0.0-beta-1

This release is a beta release that introduces .NET 10 support and
package signing. Signed packages means going forward packages can be
validated against trusted authorities that the package has been
published by Lucky Penny Software and not tampered with.

## 15.1.0

## What's Changed
* remove Microsoft.SourceLink.GitHub by @​SimonCropp in
LuckyPennySoftware/AutoMapper#4555
* Direct .NET 4.x support by @​jbogard in
LuckyPennySoftware/AutoMapper#4569
* Bumping the JsonWebTokens because of GHSA-8g4q-xg66-9fp4; fixes #​4575
by @​jbogard in
LuckyPennySoftware/AutoMapper#4576
* Provide better exceptions for errors when building the mapping plan by
@​jbogard in LuckyPennySoftware/AutoMapper#4577
* Adding docs for license configuration by @​jbogard in
LuckyPennySoftware/AutoMapper#4581
* Updating refs to fix missing method issue by @​jbogard in
LuckyPennySoftware/AutoMapper#4582

## New Contributors
* @​SimonCropp made their first contribution in
LuckyPennySoftware/AutoMapper#4555

**Full Changelog**:
LuckyPennySoftware/AutoMapper@v15.0.1...v15.1.0

## 15.0.1

## What's Changed
* Removing public signing; fixes #​4545 by @​jbogard in
LuckyPennySoftware/AutoMapper#4552
* Adding back missing overloads and reverting registering behavior by
@​jbogard in LuckyPennySoftware/AutoMapper#4554


**Full Changelog**:
LuckyPennySoftware/AutoMapper@v15.0.0...v15.0.1

This release supersedes the 15.0.0 release, reverting behavior and
overloads so that the `AddAutoMapper` overloads separate the "scanning
for maps" from the "scanning for dependencies". Unfortunately it's not
really possible to combine these two together.

This also fixes a critical bug in #​4545 that does not work with .NET
4.x applications (as intended).

Because of this, the 15.0.0 will be delisted because of the breaking
changes there.

## 15.0.0

**Full Changelog**:
LuckyPennySoftware/AutoMapper@v14.0.0...v15.0.0

* Added support for .NET Standard 2.0
* Requiring license key
* Moving from MIT license to dual commercial/OSS license

To set your license key:

```csharp
services.AddAutoMapper(cfg => {
    cfg.LicenseKey = "<License key here>";
});
```

This also introduced a breaking change with `MapperConfiguration`
requiring an `ILoggerFactory` for logging purposes:

```csharp
public MapperConfiguration(MapperConfigurationExpression configurationExpression, ILoggerFactory loggerFactory)
```

Registering AutoMapper with `services.AddAutoMapper` will automatically
supply this parameter. Otherwise you'll need to supply the logger
factory.

You can obtain your license key at
[AutoMapper.io](https://automapper.io)

Commits viewable in [compare
view](LuckyPennySoftware/AutoMapper@v14.0.0...v16.1.0).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=AutoMapper&package-manager=nuget&previous-version=14.0.0&new-version=16.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants