From 224e19528836cfb1b3b76c0d113083a728856ad7 Mon Sep 17 00:00:00 2001
From: Md Junaed Hossain <169046794+junaed-optimizely@users.noreply.github.com>
Date: Thu, 13 Nov 2025 22:46:46 +0600
Subject: [PATCH 1/5] [FSSDK-11956] CMAB Release C#
---
CHANGELOG.md | 44 +++++++++++++++++++
.../Properties/AssemblyInfo.cs | 6 +--
.../Properties/AssemblyInfo.cs | 6 +--
.../Properties/AssemblyInfo.cs | 6 +--
.../Properties/AssemblyInfo.cs | 6 +--
.../Properties/AssemblyInfo.cs | 6 +--
.../Properties/AssemblyInfo.cs | 6 +--
OptimizelySDK/Properties/AssemblyInfo.cs | 6 +--
README.md | 2 +-
9 files changed, 66 insertions(+), 22 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dca45cda..0686fcdf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,49 @@
# Optimizely C# SDK Changelog
+## 4.2.0
+Nov 13, 2025
+
+### New Features
+
+- **Added support for Contextual Multi-Armed Bandit (CMAB)**: Added support for CMAB experiments(Contextual Bandits rules) with new configuration options and cache control. To get decision from CMAB rules, `decide` and related methods must be used.
+
+#### CMAB Configuration Options
+
+The following new configuration options have been added for CMAB:
+
+```csharp
+using OptimizelySDK;
+
+// Configure CMAB settings before creating the Optimizely instance
+var cmabConfig = new CmabConfig()
+ .SetCacheSize(1000) // Optional: Set CMAB cache size (default: 1000)
+ .SetCacheTtl(TimeSpan.FromMinutes(30)); // Optional: Set CMAB cache TTL (default: 30 minutes)
+ // .SetCache(customCache) // Optional: Custom cache implementation
+
+OptimizelyFactory.SetCmabConfig(cmabConfig);
+
+var optimizely = OptimizelyFactory.NewDefaultInstance("SDK_KEY_HERE");
+```
+
+#### CMAB-Related OptimizelyDecideOptions
+
+New decide options are available to control CMAB caching behavior:
+
+- `OptimizelyDecideOption.IGNORE_CMAB_CACHE`: Bypass CMAB cache for fresh decisions
+- `OptimizelyDecideOption.RESET_CMAB_CACHE`: Clear and reset CMAB cache before making decisions
+- `OptimizelyDecideOption.INVALIDATE_USER_CMAB_CACHE`: Invalidate CMAB cache for the particular user and experiment
+
+```csharp
+using OptimizelySDK.OptimizelyDecisions;
+
+// Example usage with CMAB decide options
+var user = optimizely.CreateUserContext("user123");
+var decision = user.Decide("feature-flag-key", new[]
+{
+ OptimizelyDecideOption.IGNORE_CMAB_CACHE
+});
+```
+
## 4.1.0
November 7th, 2024
diff --git a/OptimizelySDK.DemoApp/Properties/AssemblyInfo.cs b/OptimizelySDK.DemoApp/Properties/AssemblyInfo.cs
index 147aa1a8..40772131 100644
--- a/OptimizelySDK.DemoApp/Properties/AssemblyInfo.cs
+++ b/OptimizelySDK.DemoApp/Properties/AssemblyInfo.cs
@@ -37,6 +37,6 @@
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("4.1.0.0")]
-[assembly: AssemblyFileVersion("4.1.0.0")]
-[assembly: AssemblyInformationalVersion("4.1.0")] // Used by NuGet.
+[assembly: AssemblyVersion("4.2.0.0")]
+[assembly: AssemblyFileVersion("4.2.0.0")]
+[assembly: AssemblyInformationalVersion("4.2.0")] // Used by NuGet.
diff --git a/OptimizelySDK.Net35/Properties/AssemblyInfo.cs b/OptimizelySDK.Net35/Properties/AssemblyInfo.cs
index dd5b9ab2..77c07883 100644
--- a/OptimizelySDK.Net35/Properties/AssemblyInfo.cs
+++ b/OptimizelySDK.Net35/Properties/AssemblyInfo.cs
@@ -37,6 +37,6 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("4.1.0.0")]
-[assembly: AssemblyFileVersion("4.1.0.0")]
-[assembly: AssemblyInformationalVersion("4.1.0")] // Used by NuGet.
+[assembly: AssemblyVersion("4.2.0.0")]
+[assembly: AssemblyFileVersion("4.2.0.0")]
+[assembly: AssemblyInformationalVersion("4.2.0")] // Used by NuGet.
diff --git a/OptimizelySDK.Net40/Properties/AssemblyInfo.cs b/OptimizelySDK.Net40/Properties/AssemblyInfo.cs
index 48b0cb1d..368d2d5f 100644
--- a/OptimizelySDK.Net40/Properties/AssemblyInfo.cs
+++ b/OptimizelySDK.Net40/Properties/AssemblyInfo.cs
@@ -37,6 +37,6 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("4.1.0.0")]
-[assembly: AssemblyFileVersion("4.1.0.0")]
-[assembly: AssemblyInformationalVersion("4.1.0")] // Used by NuGet.
+[assembly: AssemblyVersion("4.2.0.0")]
+[assembly: AssemblyFileVersion("4.2.0.0")]
+[assembly: AssemblyInformationalVersion("4.2.0")] // Used by NuGet.
diff --git a/OptimizelySDK.NetStandard16/Properties/AssemblyInfo.cs b/OptimizelySDK.NetStandard16/Properties/AssemblyInfo.cs
index f82a11ad..018b6364 100644
--- a/OptimizelySDK.NetStandard16/Properties/AssemblyInfo.cs
+++ b/OptimizelySDK.NetStandard16/Properties/AssemblyInfo.cs
@@ -37,6 +37,6 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("4.1.0")]
-[assembly: AssemblyFileVersion("4.1.0.0")]
-[assembly: AssemblyInformationalVersion("4.1.0")] // Used by NuGet.
+[assembly: AssemblyVersion("4.2.0")]
+[assembly: AssemblyFileVersion("4.2.0.0")]
+[assembly: AssemblyInformationalVersion("4.2.0")] // Used by NuGet.
diff --git a/OptimizelySDK.NetStandard20/Properties/AssemblyInfo.cs b/OptimizelySDK.NetStandard20/Properties/AssemblyInfo.cs
index 72640cfa..9a9f2211 100644
--- a/OptimizelySDK.NetStandard20/Properties/AssemblyInfo.cs
+++ b/OptimizelySDK.NetStandard20/Properties/AssemblyInfo.cs
@@ -37,6 +37,6 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("4.1.0.0")]
-[assembly: AssemblyFileVersion("4.1.0.0")]
-[assembly: AssemblyInformationalVersion("4.1.0")] // Used by NuGet.
+[assembly: AssemblyVersion("4.2.0.0")]
+[assembly: AssemblyFileVersion("4.2.0.0")]
+[assembly: AssemblyInformationalVersion("4.2.0")] // Used by NuGet.
diff --git a/OptimizelySDK.Tests/Properties/AssemblyInfo.cs b/OptimizelySDK.Tests/Properties/AssemblyInfo.cs
index f122893f..af81c50f 100644
--- a/OptimizelySDK.Tests/Properties/AssemblyInfo.cs
+++ b/OptimizelySDK.Tests/Properties/AssemblyInfo.cs
@@ -30,6 +30,6 @@
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("4.1.0.0")]
-[assembly: AssemblyFileVersion("4.1.0.0")]
-[assembly: AssemblyInformationalVersion("4.1.0")] // Used by NuGet.
+[assembly: AssemblyVersion("4.2.0.0")]
+[assembly: AssemblyFileVersion("4.2.0.0")]
+[assembly: AssemblyInformationalVersion("4.2.0")] // Used by NuGet.
diff --git a/OptimizelySDK/Properties/AssemblyInfo.cs b/OptimizelySDK/Properties/AssemblyInfo.cs
index 96d04867..c06a0d5d 100644
--- a/OptimizelySDK/Properties/AssemblyInfo.cs
+++ b/OptimizelySDK/Properties/AssemblyInfo.cs
@@ -41,6 +41,6 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("4.1.0.0")]
-[assembly: AssemblyFileVersion("4.1.0.0")]
-[assembly: AssemblyInformationalVersion("4.1.0")] // Used by NuGet.
+[assembly: AssemblyVersion("4.2.0.0")]
+[assembly: AssemblyFileVersion("4.2.0.0")]
+[assembly: AssemblyInformationalVersion("4.2.0")] // Used by NuGet.
diff --git a/README.md b/README.md
index 9de4ddc7..9d21c7e4 100644
--- a/README.md
+++ b/README.md
@@ -104,7 +104,7 @@ User can provide variables using following procedure:
```
+ type="OptimizelySDK.OptimizelySDKConfigSection, OptimizelySDK, Version=4.2.0.0, Culture=neutral, PublicKeyToken=null" />
```
2. Now add **optlySDKConfigSection** below ****. In this section you can add and set following **HttpProjectConfigManager** and **BatchEventProcessor** variables:
From f5f1e25c6c1754ed3374841d33a026fc35e1b05f Mon Sep 17 00:00:00 2001
From: Md Junaed Hossain <169046794+junaed-optimizely@users.noreply.github.com>
Date: Fri, 14 Nov 2025 00:06:22 +0600
Subject: [PATCH 2/5] [FSSDK-11956] pipeline
---
.github/workflows/csharp_release.yml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/csharp_release.yml b/.github/workflows/csharp_release.yml
index f4c1736c..24e8dae2 100644
--- a/.github/workflows/csharp_release.yml
+++ b/.github/workflows/csharp_release.yml
@@ -23,9 +23,11 @@ jobs:
exit 1
fi
echo "Extracted semantic version: ${SEMANTIC_VERSION}"
+ echo "Resolved git ref: ${TAG}"
+ echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "semantic_version=${SEMANTIC_VERSION}" >> $GITHUB_OUTPUT
outputs:
- tag: $TAG
+ tag: ${{ steps.set_version.outputs.tag }}
semanticVersion: ${{ steps.set_version.outputs.semantic_version }}
buildFrameworkVersions:
From 9a8a65b8d46c109b49dfb1b61574b0f5873696e0 Mon Sep 17 00:00:00 2001
From: Md Junaed Hossain <169046794+junaed-optimizely@users.noreply.github.com>
Date: Fri, 14 Nov 2025 14:51:39 +0600
Subject: [PATCH 3/5] [FSSDK-11956] manual workflow update
---
.github/workflows/csharp_release.yml | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/csharp_release.yml b/.github/workflows/csharp_release.yml
index 24e8dae2..1836c73d 100644
--- a/.github/workflows/csharp_release.yml
+++ b/.github/workflows/csharp_release.yml
@@ -3,18 +3,28 @@
on:
release:
types: [ published ] # Trigger on published pre-releases and releases
- workflow_dispatch:
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: 'Tag to build (e.g., v4.2.0)'
+ required: true
+ type: string
jobs:
variables:
name: Set Variables
runs-on: ubuntu-latest
env:
- TAG: ${{ github.event.release.tag_name }}
+ TAG: ${{ github.event.release.tag_name || inputs.tag }}
steps:
- name: Extract semantic version from tag
id: set_version
run: |
+ if [ -z "${TAG}" ]; then
+ echo "Error: No release tag available. Please provide a tag when manually triggering the workflow."
+ exit 1
+ fi
+ echo "Processing tag: ${TAG}"
# Remove the "v" prefix if it exists and extract the semantic version number
SEMANTIC_VERSION=$(echo "${TAG}" | grep -Po "(?<=^|[^0-9])([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?(-[a-zA-Z]+[0-9]*)?)")
SEMANTIC_VERSION=${SEMANTIC_VERSION#"v"}
From d96ff06b67e77cbfbfcb2724d540c15885947b20 Mon Sep 17 00:00:00 2001
From: Md Junaed Hossain <169046794+junaed-optimizely@users.noreply.github.com>
Date: Fri, 14 Nov 2025 15:30:25 +0600
Subject: [PATCH 4/5] [FSSDK-11956] making upload sequential
---
.github/workflows/csharp_release.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/csharp_release.yml b/.github/workflows/csharp_release.yml
index 1836c73d..fcdc06f8 100644
--- a/.github/workflows/csharp_release.yml
+++ b/.github/workflows/csharp_release.yml
@@ -102,7 +102,7 @@ jobs:
buildStandard16:
name: Build Standard 1.6 version
- needs: [ variables ]
+ needs: [ variables, buildFrameworkVersions ]
runs-on: windows-2022
steps:
- name: Checkout code
@@ -124,7 +124,7 @@ jobs:
buildStandard20:
name: Build Standard 2.0 version
- needs: [ variables ]
+ needs: [ variables, buildStandard16 ]
runs-on: windows-2022
steps:
- name: Checkout code
From 3625ac73d1a384972e61af701e6390f8d6ed763f Mon Sep 17 00:00:00 2001
From: Md Junaed Hossain <169046794+junaed-optimizely@users.noreply.github.com>
Date: Fri, 14 Nov 2025 15:39:38 +0600
Subject: [PATCH 5/5] [FSSDK-11956] making upload parallel + artifact name
change
---
.github/workflows/csharp_release.yml | 26 ++++++++++++++++++--------
1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/csharp_release.yml b/.github/workflows/csharp_release.yml
index fcdc06f8..adfce8bb 100644
--- a/.github/workflows/csharp_release.yml
+++ b/.github/workflows/csharp_release.yml
@@ -96,13 +96,13 @@ jobs:
- name: Upload Framework artifacts
uses: actions/upload-artifact@v4
with:
- name: unsigned-dlls
+ name: unsigned-dlls-framework
if-no-files-found: error
path: ./**/bin/Release/**/Optimizely*.dll
buildStandard16:
name: Build Standard 1.6 version
- needs: [ variables, buildFrameworkVersions ]
+ needs: [ variables ]
runs-on: windows-2022
steps:
- name: Checkout code
@@ -118,13 +118,13 @@ jobs:
- name: Upload Standard 1.6 artifact
uses: actions/upload-artifact@v4
with:
- name: unsigned-dlls
+ name: unsigned-dlls-netstandard16
if-no-files-found: error
path: ./**/bin/Release/**/Optimizely*.dll
buildStandard20:
name: Build Standard 2.0 version
- needs: [ variables, buildStandard16 ]
+ needs: [ variables ]
runs-on: windows-2022
steps:
- name: Checkout code
@@ -137,10 +137,10 @@ jobs:
run: dotnet restore OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj
- name: Build and strongly name Standard 2.0 project
run: dotnet build OptimizelySDK.NetStandard20/OptimizelySDK.NetStandard20.csproj /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=$(pwd)/keypair.snk -c Release
- - name: Build and strongly name assemblies
+ - name: Upload Standard 2.0 artifacts
uses: actions/upload-artifact@v4
with:
- name: unsigned-dlls
+ name: unsigned-dlls-netstandard20
if-no-files-found: error
path: ./**/bin/Release/**/Optimizely*.dll
@@ -158,10 +158,20 @@ jobs:
# TODO: Remove this when we're ready to automate
- name: Temporarily halt progress
run: exit 1
- - name: Download the unsigned files
+ - name: Download Framework DLLs
+ uses: actions/download-artifact@v4
+ with:
+ name: unsigned-dlls-framework
+ path: ./unsigned-dlls
+ - name: Download NetStandard 1.6 DLLs
+ uses: actions/download-artifact@v4
+ with:
+ name: unsigned-dlls-netstandard16
+ path: ./unsigned-dlls
+ - name: Download NetStandard 2.0 DLLs
uses: actions/download-artifact@v4
with:
- name: unsigned-dlls
+ name: unsigned-dlls-netstandard20
path: ./unsigned-dlls
- name: Setup SSH
uses: shimataro/ssh-key-action@v2