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
16 changes: 16 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.git
.github
**/.vs
**/bin
**/obj
**/*.user
**/*.suo

# Components not needed for Example.WebApp image
dxa-framework-datamodel
dxa-framework-mvc-net
dxa-module-dynamicdocumentation-net
dxa-module-search-net
dxa-pca-client-net

# Keep LocalNugetStorage, Directory.Build.props, dxa-web-application-mvc-net, dxa-module-core-net
38 changes: 34 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Setup .NET 8
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
dotnet-version: |
8.0.x
10.0.x

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
Expand All @@ -32,10 +34,38 @@ jobs:
restore-keys: |
${{ runner.os }}-nuget-

# Builds every DXA component in dependency order (datamodel -> pca-client -> framework-mvc),
# packs clean 2.4.0 nupkgs, and primes the shared LocalNugetStorage so downstream restores
# Builds every DXA component in dependency order (datamodel -> pca-client -> framework-mvc -> modules),
# packs clean 2.4.1 nupkgs, and primes the shared LocalNugetStorage so downstream restores
# resolve locally. -SkipPush keeps it a CI build (no Nexus push). -SkipSign avoids the
# signing step which needs a cert that isn't on CI runners.
- name: Build & pack all DXA packages
shell: pwsh
run: .\Release-Dxa.ps1 -SkipPush -SkipSign -NonInteractive

# Prove modules + Example WebApp compile for both net8.0 and net10.0 against the packed packages.
- name: Build modules and Example WebApp
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$restoreSource = Join-Path $PWD 'LocalNugetStorage'
$projects = @(
'dxa-module-core-net\dotnet\src\Tridion.Dxa.Module.Core\Tridion.Dxa.Module.Core.csproj',
'dxa-module-search-net\dotnet\src\Tridion.Dxa.Module.Search\Tridion.Dxa.Module.Search.csproj',
'dxa-module-dynamicdocumentation-net\dotnet\src\Tridion.Dxa.Module.DynamicDocumentation\Tridion.Dxa.Module.DynamicDocumentation.csproj',
'dxa-web-application-mvc-net\dotnet\src\Tridion.Dxa.Example.WebApp\Tridion.Dxa.Example.WebApp.csproj'
)
foreach ($p in $projects) {
Write-Host "==== Building $p ====" -ForegroundColor Cyan
dotnet build $p -c Release /p:RestoreAdditionalProjectSources="$restoreSource"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}

- name: Publish Example WebApp (net8.0 and net10.0)
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$webapp = 'dxa-web-application-mvc-net\dotnet\src\Tridion.Dxa.Example.WebApp\Tridion.Dxa.Example.WebApp.csproj'
dotnet publish $webapp -c Release -f net8.0 -o artifacts/webapp-net8 --no-build
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
dotnet publish $webapp -c Release -f net10.0 -o artifacts/webapp-net10 --no-build
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ ScaffoldingReadMe.txt
# Local NuGet feed folders (produced by Release-Dxa.ps1 and build.proj GenerateLocalNuGetPackages target)
**/LocalNugetStorage/

# Generated CM test Dockerfiles (New-CmTestDockerfile.ps1)
**/Dockerfile*.cmtest

# Others
~$*
*~
Expand Down
24 changes: 24 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>

<!-- Shared Microsoft package bands per TFM -->
<MicrosoftAspNetCorePackageVersion Condition="'$(TargetFramework)' == 'net8.0'">8.0.19</MicrosoftAspNetCorePackageVersion>
<MicrosoftAspNetCorePackageVersion Condition="'$(TargetFramework)' == 'net10.0'">10.0.11</MicrosoftAspNetCorePackageVersion>

<MicrosoftExtensionsConfigurationVersion Condition="'$(TargetFramework)' == 'net8.0'">8.0.0</MicrosoftExtensionsConfigurationVersion>
<MicrosoftExtensionsConfigurationVersion Condition="'$(TargetFramework)' == 'net10.0'">10.0.11</MicrosoftExtensionsConfigurationVersion>

<MicrosoftExtensionsConfigurationBinderVersion Condition="'$(TargetFramework)' == 'net8.0'">8.0.2</MicrosoftExtensionsConfigurationBinderVersion>
<MicrosoftExtensionsConfigurationBinderVersion Condition="'$(TargetFramework)' == 'net10.0'">10.0.11</MicrosoftExtensionsConfigurationBinderVersion>

<MicrosoftExtensionsDependencyInjectionVersion Condition="'$(TargetFramework)' == 'net8.0'">8.0.1</MicrosoftExtensionsDependencyInjectionVersion>
<MicrosoftExtensionsDependencyInjectionVersion Condition="'$(TargetFramework)' == 'net10.0'">10.0.11</MicrosoftExtensionsDependencyInjectionVersion>

<MicrosoftExtensionsHostingVersion Condition="'$(TargetFramework)' == 'net8.0'">8.0.1</MicrosoftExtensionsHostingVersion>
<MicrosoftExtensionsHostingVersion Condition="'$(TargetFramework)' == 'net10.0'">10.0.11</MicrosoftExtensionsHostingVersion>

<SystemServiceModelSyndicationVersion Condition="'$(TargetFramework)' == 'net8.0'">8.0.0</SystemServiceModelSyndicationVersion>
<SystemServiceModelSyndicationVersion Condition="'$(TargetFramework)' == 'net10.0'">10.0.11</SystemServiceModelSyndicationVersion>
</PropertyGroup>
</Project>
46 changes: 46 additions & 0 deletions NuGetApiToken.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Create a NuGet.org API token

Use a **nuget.org API key** when you push a stable DXA release to the public feed with `Release-Dxa.ps1`. Do not use the default Nexus key from `build.proj` against nuget.org.

Official reference: [Create API keys](https://learn.microsoft.com/en-us/nuget/nuget-org/publish-a-package#create-api-keys).

## Prerequisites

- A [nuget.org](https://www.nuget.org/) account that is allowed to publish `Tridion.Dxa.*` packages (organization membership as required by RWS).
- Two-factor authentication enabled on that account (required by nuget.org).

## Create the key

1. Sign in at [https://www.nuget.org/](https://www.nuget.org/).
2. Open **API Keys**: [https://www.nuget.org/account/apikeys](https://www.nuget.org/account/apikeys) (or select your username, then **API Keys**).
3. Select **Create**.
4. Fill in:
- **Key name** — for example `dxa-core-push-YYYY-MM`.
- **Expires** — pick a short lifetime (for example 1 year or less). Rotate before expiry.
- **Glob pattern** — `Tridion.Dxa.*` (limits the key to DXA package IDs).
- **Select scopes** — enable **Push** (new packages and new versions). Enable **Unlist** only if you must unlist a bad package.
5. Select **Create**. Copy the key immediately. nuget.org shows the full value **once**.

Store the key in a password manager or a CI secret. Never commit it to git, paste it into `build.proj`, or share it in chat.

## Use the key

Push a verified stable build to nuget.org:

```powershell
.\Release-Dxa.ps1 -NuGetSource https://api.nuget.org/v3/index.json -ApiKey <nuget-org-key>
```

Replace `<nuget-org-key>` with the value you copied. Preview releases (`-Preview`) stay on internal Nexus and must not use nuget.org.

To store the key locally for `dotnet nuget` (optional):

```powershell
dotnet nuget setapikey <nuget-org-key> --source https://api.nuget.org/v3/index.json
```

## If a key is leaked

1. On [API Keys](https://www.nuget.org/account/apikeys), **Regenerate** or **Remove** the leaked key.
2. Create a new key with the same glob and scopes.
3. Update any CI secrets or local `setapikey` entries.
116 changes: 104 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Build status

Prerequisites
-------------
For building .NET 8 repositories you must have the following installed:
- Visual Studio 2022
- .NET 8
For building .NET 8 and .NET 10 repositories you must have the following installed:
- Visual Studio 2022 or higher
- .NET 8 and .NET 10 SDKs

Build (single component)
------------------------
Expand All @@ -28,12 +28,13 @@ The `Release-Dxa.ps1` script at the repo root drives a full multi-package releas
1. **Tridion.Dxa.Framework.DataModel** (from `dxa-framework-datamodel`)
2. **Tridion.Dxa.Api.Client** (from `dxa-pca-client-net`)
3. **Tridion.Dxa.Framework** (from `dxa-framework-mvc-net`) — package refs for #1 and #2 are auto-bumped before this builds.
4. Bumps the `Tridion.Dxa.Framework` `<PackageReference>` in each consumer csproj: `dxa-module-core-net`, `dxa-module-dynamicdocumentation-net`, `dxa-module-search-net`, `dxa-web-application-mvc-net`.
4. **Tridion.Dxa.Module.Core**, **Tridion.Dxa.Module.Search**, **Tridion.Dxa.Module.DynamicDocumentation** — Framework package refs are auto-bumped before each module builds.
5. Bumps the `Tridion.Dxa.Framework` `<PackageReference>` in the Example WebApp (`dxa-web-application-mvc-net`).

Each package step:
- `msbuild build.proj /t:Build` (uses the existing per-component build target)
- `msbuild build.proj /t:SignAssemblies` (skip with `-SkipSign` on machines without a code-signing cert)
- `dotnet pack` with `/p:VersionSuffix=""` to produce a clean stable `2.4.0.nupkg` (the default `SignPackAndPushNuGetPackages` target stamps a `beta-{timestamp}` suffix; we bypass it)
- `dotnet pack` with `/p:VersionSuffix=""` to produce a clean stable `2.4.1.nupkg` (the default `SignPackAndPushNuGetPackages` target stamps a `beta-{timestamp}` suffix; we bypass it)
- `dotnet nuget push` to the target feed (skip with `-SkipPush`)
- Copies the produced `.nupkg` into the shared `LocalNugetStorage/` at the repo root, and the next component's restore picks it up via `/p:RestoreAdditionalProjectSources` (no Nexus propagation delay)

Expand All @@ -46,37 +47,128 @@ Each package step:
# Local smoke test: builds & packs all components, doesn't push to any feed.
.\Release-Dxa.ps1 -SkipPush

# Full release to internal Nexus.
# Full stable release of 2.4.1 to internal Nexus.
.\Release-Dxa.ps1

# After Nexus has been verified, re-publish to public NuGet.org.
# Preview release to internal Nexus only (e.g. 2.4.1-preview-20260821122600).
.\Release-Dxa.ps1 -Preview -NonInteractive

# After Nexus has been verified, re-publish a stable build to public NuGet.org.
# Create the API key first: see NuGetApiToken.md
.\Release-Dxa.ps1 -NuGetSource https://api.nuget.org/v3/index.json -ApiKey <nuget-org-key>
```

Create a nuget.org API token before a public push: [NuGetApiToken.md](NuGetApiToken.md).

### Parameters

| Parameter | Default | Purpose |
|---|---|---|
| `-Version` | `2.4.0` | Stable version. Becomes `VersionPrefix`; `VersionSuffix` is forced empty. |
| `-Version` | `2.4.1` | Version prefix (`VersionPrefix`). Stable packs with empty suffix; `-Preview` appends `preview-{timestamp}`. |
| `-Preview` | `false` | Pack/push `{Version}-preview-{yyyyMMddHHmmss}` to Nexus only (refuses nuget.org). |
| `-NuGetSource` | Internal Nexus URL | Target feed for `dotnet nuget push`. |
| `-ApiKey` | `(from build.proj)` | API key for push. |
| `-ApiKey` | `(from build.proj)` | API key for push. For nuget.org, create a token as in [NuGetApiToken.md](NuGetApiToken.md). |
| `-SkipSign` | `false` | Skip `SignAssemblies` target. |
| `-SkipPush` | `false` | Build & pack only; do not push. |
| `-DryRun` | `false` | Print commands without executing. |
| `-NonInteractive` | `false` | Skip per-stage confirmation prompts (used by CI). |

### After a successful release

1. Smoke-test a clean restore in a downstream consumer (e.g. `dxa-web-application-mvc-net`) to confirm `2.4.0` resolves from the target feed.
1. Smoke-test a clean restore in a downstream consumer (e.g. `dxa-web-application-mvc-net`) to confirm `2.4.1` resolves from the target feed.
2. Commit the csproj reference bumps the script made:
```
git add -- '**/*.csproj'
git commit -m 'Release 2.4.0'
git tag v2.4.0
git commit -m 'Release 2.4.1'
git tag v2.4.1
git push origin develop --tags
```
3. Update release notes.

Docker (Example WebApp)
-----------------------
The Example WebApp multi-targets `net8.0` and `net10.0`. Build **one image per TFM** from the repo root (after packing packages into `LocalNugetStorage/` via `.\Release-Dxa.ps1 -SkipPush -SkipSign -NonInteractive`):

```powershell
# .NET 8
docker build -f dxa-web-application-mvc-net/dotnet/src/Tridion.Dxa.Example.WebApp/Dockerfile -t dxa-example-webapp:net8 .

# .NET 10
docker build -f dxa-web-application-mvc-net/dotnet/src/Tridion.Dxa.Example.WebApp/Dockerfile.net10.0 -t dxa-example-webapp:net10 .
```

ARM runtime images (publish output copied in separately):

- `ARM.Dockerfile` — ASP.NET 8.0 Alpine arm64v8
- `ARM.Dockerfile.net10.0` — ASP.NET 10.0 Alpine arm64v8

Folder publish profiles:

- `Properties/PublishProfiles/FolderProfile.pubxml` → `net8.0`
- `Properties/PublishProfiles/FolderProfile.net10.0.pubxml` → `net10.0`

Migrating from .NET 8 to .NET 10
-------------------------------

### For consumers using published NuGet packages

Use this path if your site references `Tridion.Dxa.*` from Nexus or NuGet.org and you do not build DXA from this repository.

**Prerequisites**
- Install the [.NET 10 SDK](https://dotnet.microsoft.com/download) for builds; install the ASP.NET Core 10 runtime on deploy hosts.
- Use DXA packages **2.4.1 or later**. Those packages multi-target `net8.0` and `net10.0` (`lib/net8.0` and `lib/net10.0`). Packages built only for `net8.0` will not provide a net10 asset.

**Steps**
1. In your web application `.csproj`, change the target framework to .NET 10:
```xml
<TargetFramework>net10.0</TargetFramework>
```
(Or multi-target `net8.0;net10.0` if you must support both during a transition.)
2. Update all `Tridion.Dxa.*` package references to **2.4.1+**, for example:
```xml
<PackageReference Include="Tridion.Dxa.Framework" Version="2.4.1" />
<PackageReference Include="Tridion.Dxa.Module.Core" Version="2.4.1" />
```
Repeat for any other DXA modules you use (`Module.Search`, `Module.DynamicDocumentation`, etc.). Prefer:
```powershell
dotnet add package Tridion.Dxa.Framework --version 2.4.1
```
3. Bump Microsoft ASP.NET Core / Extensions packages your app references to **10.0.x** (match the shared framework). Do not keep AspNetCore **8.0.x** packages on a `net10.0` app.
4. Restore, build, and publish for net10:
```powershell
dotnet restore
dotnet build -c Release -f net10.0
dotnet publish -c Release -f net10.0 -o ./publish
```
5. Deploy onto hosts or containers that run the **ASP.NET Core 10** runtime (not the .NET 8 runtime image).

NuGet resolves `lib/net10.0` from the DXA packages automatically when your project targets `net10.0`—no extra package IDs or TFM suffixes are required.

**Smoke checklist**
- Restore succeeds against your feed (Nexus / NuGet.org)
- App builds and starts on ASP.NET Core 10
- Pages render; DXA modules initialize as before

**Known warnings (non-blocking)**
Building against net10 may surface ASP.NET deprecation warnings. They do not fail the build unless warnings-as-errors is enabled. Suggested resolutions:

| ID | API | Where it appears in DXA | Resolution |
| --- | --- | --- | --- |
| `ASPDEPR003` | Razor runtime compilation (`AddRazorRuntimeCompilation`, `MvcRazorRuntimeCompilationOptions`) | Example web app `Startup.cs`; framework `AddDxaModule` (embedded module views) | Prefer build-time view compilation (`*.Views.dll` / `CompiledRazorAssemblyPart`, already used when a views assembly exists). In Development, use Hot Reload instead of runtime compilation. Restrict `AddRazorRuntimeCompilation` to Development only if you still need on-disk or embedded `.cshtml` edits without a rebuild. Runtime compilation is not recommended in production. |
| `ASPDEPR005` | `ForwardedHeadersOptions.KnownNetworks` / `Microsoft.AspNetCore.HttpOverrides.IPNetwork` | Example web app `LoggingMiddleware` (trace logging of forwarded-header options) | Log `KnownIPNetworks` and `System.Net.IPNetwork` instead of `KnownNetworks`. If you configure trusted proxy ranges, add them to `KnownIPNetworks` (prefix host bits must be zero). |
| `ASPDEPR006` | `IActionContextAccessor` / `ActionContextAccessor` | Framework `AddDxa` / `AddDxaWebApi` registration; `HtmlHelperExtensions.Action` nested action render | Microsoft’s replacement is `IHttpContextAccessor` plus `HttpContext.GetEndpoint()` for *reading* action metadata. DXA still *writes* a child `ActionContext` while rendering nested `Html.Action` calls, so a full drop-in is not available yet. Planned path: invoke via `IActionInvokerFactory` on an explicit `ActionContext`, set the child `HttpContext` (and endpoint metadata) on `IHttpContextAccessor`, and remove the accessor registration. Until then, keep the current usage; it remains functional on net10. |

See also: [ASPDEPR003](https://aka.ms/aspnet/deprecate/003), [ASPDEPR005](https://aka.ms/aspnet/deprecate/005), [ASPDEPR006](https://aka.ms/aspnet/deprecate/006).

### Building or deploying this repository

For people working in `dxa-core` itself (not required for NuGet-only consumers):

- Folder publish: `-f net10.0` or `FolderProfile.net10.0.pubxml`
- x64 Docker: `Dockerfile.net10.0`
- ARM: `ARM.Dockerfile.net10.0`
- Shared Microsoft package bands for net10 live in the root `Directory.Build.props`

About
-----
The RWS Digital Experience Accelerator (DXA) is a reference implementation of RWS Tridion Sites 10+ intended to help you create, design and publish an RWS Tridion/Web-based website quickly.
Expand Down
Loading
Loading