Skip to content

Support LicenseKey via environment variable fallback#4634

Open
Nit8 wants to merge 5 commits into
LuckyPennySoftware:mainfrom
Nit8:main
Open

Support LicenseKey via environment variable fallback#4634
Nit8 wants to merge 5 commits into
LuckyPennySoftware:mainfrom
Nit8:main

Conversation

@Nit8

@Nit8 Nit8 commented Apr 25, 2026

Copy link
Copy Markdown

PR: Auto-Detect License Key from Environment Variables Fixes #4631

What Changed

Added support for reading the AutoMapper license key from an environment variable instead of requiring manual configuration in code.

Why

In containerized and cloud environments, it's better to use environment variables for sensitive configuration instead of hardcoding values in code.

How It Works

The LicenseKey property now checks for values in this order:

  1. Explicit value set in code (cfg.LicenseKey = "key") - highest priority
  2. Environment variable AUTOMAPPER_LICENSE_KEY - fallback
  3. Null if neither is set - lowest priority

Usage

NEW WAY - Using Environment Variable:
Set environment variable AUTOMAPPER_LICENSE_KEY before running your app
Then just add AutoMapper without setting the license key:

services.AddAutoMapper(cfg => 
{
    cfg.CreateMap<Foo, FooDto>();
});

OLD WAY - Still Works (Backward Compatible):

services.AddAutoMapper(cfg => 
{
    cfg.LicenseKey = "your-license-key";
    cfg.CreateMap<Foo, FooDto>();
});

BOTH SET - Code value wins:
If you set both the environment variable AND code value, the code value is used.

Tests Added

  • Tests for environment variable auto-detection
  • Tests for old explicit assignment (backward compatibility)
  • Tests for precedence when both are set
  • Integration tests with dependency injection

Backward Compatibility

100% backward compatible. All existing code continues to work exactly the same way.

LicenseKey now falls back to the AUTOMAPPER_LICENSE_KEY environment variable if not explicitly set. Added comprehensive unit and integration tests to verify this behavior. Updated test project dependencies to support DI and logging in tests.
@Nit8

Nit8 commented Apr 29, 2026

Copy link
Copy Markdown
Author

@jbogard can you run this pipeline to check if anything goes wrong

@Nit8

Nit8 commented May 1, 2026

Copy link
Copy Markdown
Author

Hi @jbogard, is this issue related to pipeline, am I missing something?

@jbogard

jbogard commented May 1, 2026

Copy link
Copy Markdown
Contributor

Hmm that is odd, I tried to re-run it but still getting that error.

@Nit8

Nit8 commented May 4, 2026

Copy link
Copy Markdown
Author

yeah @jbogard thats strange does it have something to do with ci pipeline and about read and write permissions?

@Nit8

Nit8 commented May 19, 2026

Copy link
Copy Markdown
Author

Hi @jbogard I see you changed the permission into your release.yml and maybe same has to be done from ci.yml from read to write for this to complete as mentioned in my previous comment, and you can try firing the pipeline to verify. Thanks

@SubashPaudyalTR

Copy link
Copy Markdown

This would be a great relief if we have this directly fed from the configuration, and introduce less noise ... as we are having more than dozens of services which is using automapper and we just acquired the enterprise license. We are looking for best option weather to go with per service configuration v/s one-place shared library. Both holds its own pros and cons, but if we get this env. var auto-discovery, it would be the real winner for us... with minimal changes.

Waiting for this and any other valuable suggestions!!!

@jbogard

jbogard commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Hey all, I want this to land in the next release. I need this to also work with MediatR so I'm going to suggest some changes that allow it to work in environments that use a single Bundle key across both products in a single application.

Move environment-variable fallback out of the MapperConfigurationExpression
property getter and into LicenseAccessor, the single consumer, keeping the
config expression a plain POCO. Add a second, shared LUCKYPENNY_LICENSE_KEY
environment variable so the same Lucky Penny bundle key can be used across
products (e.g. MediatR).

Resolution precedence: explicit cfg.LicenseKey > AUTOMAPPER_LICENSE_KEY >
LUCKYPENNY_LICENSE_KEY (product-specific wins, matching Duende's two-tier
"most-specific config key wins" model).

Tests now target LicenseAccessor.ResolveLicenseKey directly for precedence,
and the class disables parallelization since it mutates process-global
environment variables. Docs updated with the auto-discovery section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jbogard

jbogard commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Thanks for this, @Nit8 — really appreciate the contribution, and it pushed exactly the right feature forward. 🙏 I've pushed a few commits on top of your branch to get it merge-ready, and wanted to walk through what changed and why.

What I kept: your core idea — falling back to an environment variable when no key is set in code, fully backward compatible — is exactly right, and the precedence model (explicit code value wins) is preserved.

What I changed:

  1. Moved the discovery out of the property getter into LicenseAccessor. Reading Environment.GetEnvironmentVariable(...) inside the LicenseKey getter made the property side-effecting and non-deterministic (it hit the environment on every access). I reverted MapperConfigurationExpression.LicenseKey back to a plain auto-property and centralized resolution in LicenseAccessor.ResolveLicenseKey(...) — the single place that actually consumes the key. This also gives us a clean seam for the IConfiguration / file-based discovery that Support automatic discovery of license keys #4631 also asks for.

  2. Added a second, shared LUCKYPENNY_LICENSE_KEY variable. Lucky Penny licenses are sold as a bundle covering both AutoMapper and MediatR, so a company-wide variable lets both libraries read the same key. Resolution precedence is now:

    • cfg.LicenseKey (explicit code) →
    • AUTOMAPPER_LICENSE_KEY (product-specific) →
    • LUCKYPENNY_LICENSE_KEY (shared)

    Product-specific wins over the shared key, mirroring how Duende IdentityServer layers a product key over a company-wide one. (Note: the shared key still has to be for a license that includes AutoMapper — a MediatR-only license won't validate here.)

  3. Reworked the tests to target ResolveLicenseKey directly for the precedence rules, and disabled parallelization on that class since it mutates process-global environment variables.

  4. Documented the new auto-discovery section in License-configuration.md.

The env var names follow the flat all-caps convention used by other commercial .NET libraries (Particular's PARTICULARSOFTWARE_LICENSE, Telerik's TELERIK_LICENSE, etc.), since AutoMapper reads them directly rather than through IConfiguration binding.

Thanks again — your authorship on the PR stands, this just builds on it.

jbogard and others added 2 commits June 10, 2026 11:18
dorny/test-reporter needs checks:write to create its report check-run, but
GitHub forces GITHUB_TOKEN to read-only on pull_request runs from forks, so
the step errored and failed the whole job even though all tests passed. Mark
both report steps continue-on-error so report publishing is best-effort; the
Build and Test step already reflects actual pass/fail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The build-windows job authenticated to Azure via OIDC on every run, but the
Azure secrets aren't exposed to pull requests (empty on forks), so the login
step failed PR builds. Azure login and package signing only matter for the
main-branch publish path, so gate both to github.ref == 'refs/heads/main',
matching the existing Push to MyGet guard. Windows still builds and tests on
PRs; it just skips the publish-only steps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Nit8 Nit8 left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks so much for taking the time to build on this rather than just closing it really means a lot.
The refactor to LicenseAccessor.ResolveLicenseKey() makes complete sense in hindsight; I hadn't considered the side-effect problem with hitting the environment on every property access.
Happy with everything you've done on top, and glad the core idea was worth building on. Looking forward to seeing it merged this is a library we use in our own project, and having run into this issue ourselves, it felt worth solving for everyone's benefit.
Looking forward to contributing more to this amazing project!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support automatic discovery of license keys

3 participants