Skip to content

[DRAFT] Canary task rollouts #20867

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion BuildConfigGen/Debugging/VsCodeLaunchConfigGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ public void AddForTask(string taskConfigPath)
int major = versionNode["Major"]!.GetValue<int>();
int minor = versionNode["Minor"]!.GetValue<int>();
int patch = versionNode["Patch"]!.GetValue<int>();
string? build = versionNode["Build"]?.GetValue<string>() ?? null;

var version = new TaskVersion(major, minor, patch);
var version = new TaskVersion(major, minor, patch, build);

LaunchConfig.AddConfigForTask(
taskId: taskConfig["id"]!.GetValue<string>(),
Expand Down
116 changes: 109 additions & 7 deletions BuildConfigGen/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal static class Config
{
public static readonly string[] ExtensionsToPreprocess = new[] { ".ts", ".json" };

public record ConfigRecord(string name, string constMappingKey, bool isDefault, bool isNode, string nodePackageVersion, bool isWif, string nodeHandler, string preprocessorVariableName, bool enableBuildConfigOverrides, bool deprecated, bool shouldUpdateTypescript, bool writeNpmrc, string? overriddenDirectoryName = null, bool shouldUpdateLocalPkgs = false, bool useGlobalVersion = false, bool useAltGeneratedPath = false, bool mergeToBase = false);
public record ConfigRecord(string name, string constMappingKey, bool isDefault, bool isNode, string nodePackageVersion, bool isWif, string nodeHandler, string preprocessorVariableName, bool enableBuildConfigOverrides, bool deprecated, bool shouldUpdateTypescript, bool writeNpmrc, string? overriddenDirectoryName = null, bool shouldUpdateLocalPkgs = false, bool useGlobalVersion = false, bool useAltGeneratedPath = false, bool mergeToBase = false, bool abTaskReleases = true);

public static readonly ConfigRecord Default = new ConfigRecord(name: nameof(Default), constMappingKey: "Default", isDefault: true, isNode: false, nodePackageVersion: "", isWif: false, nodeHandler: "", preprocessorVariableName: "DEFAULT", enableBuildConfigOverrides: false, deprecated: false, shouldUpdateTypescript: false, writeNpmrc: false);
public static readonly ConfigRecord Node16 = new ConfigRecord(name: nameof(Node16), constMappingKey: "Node16-219", isDefault: false, isNode: true, nodePackageVersion: "^16.11.39", isWif: false, nodeHandler: "Node16", preprocessorVariableName: "NODE16", enableBuildConfigOverrides: true, deprecated: true, shouldUpdateTypescript: false, writeNpmrc: false);
Expand Down Expand Up @@ -69,12 +69,22 @@ public record ConfigRecord(string name, string constMappingKey, bool isDefault,
/// <param name="getTaskVersionTable"></param>
/// <param name="debugAgentDir">When set to the local pipeline agent directory, this tool will produce tasks in debug mode with the corresponding visual studio launch configurations that can be used to attach to built tasks running on this agent</param>
/// <param name="includeLocalPackagesBuildConfig">Include LocalPackagesBuildConfig</param>
static void Main(string? task = null, string? configs = null, int? currentSprint = null, bool writeUpdates = false, bool allTasks = false, bool getTaskVersionTable = false, string? debugAgentDir = null, bool includeLocalPackagesBuildConfig = false)
/// <param name="useSemverBuildConfig">If true, the semver "build" (suffix) will be generated for each task configuration produced, but all tasks configurations will have the same version (for example '1.2.3-node20' and 1.2.3-wif). The default configuration gets no build suffix (e.g. 1.2.3).</param>
static void Main(
string? task = null,
string? configs = null,
int? currentSprint = null,
bool writeUpdates = false,
bool allTasks = false,
bool getTaskVersionTable = false,
string? debugAgentDir = null,
bool includeLocalPackagesBuildConfig = false,
bool useSemverBuildConfig = false)
{
try
{
ensureUpdateModeVerifier = new EnsureUpdateModeVerifier(!writeUpdates);
MainInner(task, configs, currentSprint, writeUpdates, allTasks, getTaskVersionTable, debugAgentDir, includeLocalPackagesBuildConfig);
MainInner(task, configs, currentSprint, writeUpdates, allTasks, getTaskVersionTable, debugAgentDir, includeLocalPackagesBuildConfig, useSemverBuildConfig);
}
catch (Exception e2)
{
Expand All @@ -96,7 +106,16 @@ static void Main(string? task = null, string? configs = null, int? currentSprint
}
}

private static void MainInner(string? task, string? configs, int? currentSprintNullable, bool writeUpdates, bool allTasks, bool getTaskVersionTable, string? debugAgentDir, bool includeLocalPackagesBuildConfig)
private static void MainInner(
string? task,
string? configs,
int? currentSprintNullable,
bool writeUpdates,
bool allTasks,
bool getTaskVersionTable,
string? debugAgentDir,
bool includeLocalPackagesBuildConfig,
bool useSemverBuildConfig)
{
if (allTasks)
{
Expand Down Expand Up @@ -290,7 +309,18 @@ private static void MainInner(string? task, string? configs, int? currentSprintN
{
IEnumerable<string> configsList = FilterConfigsForTask(configs, t);

MainUpdateTask(taskVersionInfo[t.Value.Name], t.Value.Name, configsList, writeUpdates, currentSprint, debugConfGen, includeLocalPackagesBuildConfig, hasGlobalVersion: globalVersion is not null, generatedFolder: generatedFolder, altGeneratedFolder: altGeneratedFolder);
MainUpdateTask(
taskVersionInfo[t.Value.Name],
t.Value.Name,
configsList,
writeUpdates,
currentSprint,
debugConfGen,
includeLocalPackagesBuildConfig,
hasGlobalVersion: globalVersion is not null,
generatedFolder: generatedFolder,
altGeneratedFolder: altGeneratedFolder,
useSemverBuildConfig: useSemverBuildConfig);
}

debugConfGen.WriteLaunchConfigurations();
Expand Down Expand Up @@ -514,7 +544,8 @@ private static void MainUpdateTask(
bool includeLocalPackagesBuildConfig,
bool hasGlobalVersion,
string generatedFolder,
string altGeneratedFolder)
string altGeneratedFolder,
bool useSemverBuildConfig)
{
if (string.IsNullOrEmpty(task))
{
Expand Down Expand Up @@ -557,6 +588,9 @@ private static void MainUpdateTask(
// we need to ensure merges occur first, as the changes may cascade to other configs (e.g. Default), if there are multiple
var targetConfigsWithMergeToBaseOrderedFirst = targetConfigs.OrderBy(x => x.mergeToBase ? 0 : 1);

var defaultConfig = targetConfigs.FirstOrDefault(x => x.isDefault)
?? throw new ArgumentException($"There is no default config for task {task} which is required if {nameof(useSemverBuildConfig)} is true");

foreach (var config in targetConfigsWithMergeToBaseOrderedFirst)
{
if (config.useGlobalVersion && !includeLocalPackagesBuildConfig)
Expand Down Expand Up @@ -661,7 +695,12 @@ private static void MainUpdateTask(
WriteWIFInputTaskJson(taskOutput, config, "task.json", isLoc: false);
WriteWIFInputTaskJson(taskOutput, config, "task.loc.json", isLoc: true);

if (!config.mergeToBase)
if (useSemverBuildConfig)
{
WriteTaskJsonWithSemverConfig(taskOutput, taskVersionState, defaultConfig, config, "task.json", existingLocalPackageVersion);
WriteTaskJsonWithSemverConfig(taskOutput, taskVersionState, defaultConfig, config, "task.loc.json", existingLocalPackageVersion);
}
else if (!config.mergeToBase)
{
WriteTaskJson(taskOutput, taskVersionState, config, "task.json", existingLocalPackageVersion);
WriteTaskJson(taskOutput, taskVersionState, config, "task.loc.json", existingLocalPackageVersion);
Expand Down Expand Up @@ -1032,6 +1071,62 @@ private static void WriteTaskJson(string taskPath, TaskStateStruct taskState, Co
ensureUpdateModeVerifier!.WriteAllText(outputTaskPath, outputTaskNode.ToJsonString(jso), suppressValidationErrorIfTargetPathDoesntExist: false);
}

/// <summary>
/// This uses the same major.minor.patch for all build configuration tasks, but the "build" suffix of semver is different, and directly corresponds to the name.
/// We no longer populate the '_buildConfigMapping' property of the task.json, since server won't expect this property to be set.
/// </summary>
/// <param name="taskPath"></param>
/// <param name="taskState"></param>
/// <param name="defaultConfig"></param>
/// <param name="config"></param>
/// <param name="fileName"></param>
/// <param name="existingLocalPackageVersion"></param>
private static void WriteTaskJsonWithSemverConfig(string taskPath,
Copy link
Contributor

Choose a reason for hiding this comment

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

Better to combine this with WriteTaskJson, and just add an if statement to opt into "withSemverConfig")

TaskStateStruct taskState,
Config.ConfigRecord defaultConfig,
Config.ConfigRecord config,
string fileName,
string? existingLocalPackageVersion)
{
string outputTaskPath = Path.Combine(taskPath, fileName);
JsonNode outputTaskNode = JsonNode.Parse(ensureUpdateModeVerifier!.FileReadAllText(outputTaskPath))!;

outputTaskNode["version"]!["Major"] = taskState.configTaskVersionMapping[config].Major;
outputTaskNode["version"]!["Minor"] = taskState.configTaskVersionMapping[config].Minor;
outputTaskNode["version"]!["Patch"] = taskState.configTaskVersionMapping[config].Patch;

if (defaultConfig != config)
{
outputTaskNode["version"]!["Build"] = config.constMappingKey;
}

var outputTaskNodeObject = outputTaskNode.AsObject();
outputTaskNodeObject.Remove("_buildConfigMapping");

bool anyVersionsUpdatedExceptForGlobal = taskState.versionsUpdated.Where(x => !x.useGlobalVersion).Any();

JsonObject configMapping = new JsonObject();
var configTaskVersionMappingSortedByConfig = taskState.configTaskVersionMapping.OrderBy(x => x.Key.name);
foreach (var cfg in configTaskVersionMappingSortedByConfig)
{
if (!config.useGlobalVersion && cfg.Key.useGlobalVersion && !anyVersionsUpdatedExceptForGlobal)
{
if (existingLocalPackageVersion != null)
{
configMapping.Add(new(cfg.Key.constMappingKey, existingLocalPackageVersion));
}
}
else
{
configMapping.Add(new(cfg.Key.constMappingKey, cfg.Value.ToString()));
}
}

outputTaskNode.AsObject().Add("_buildConfigMapping", configMapping);

ensureUpdateModeVerifier!.WriteAllText(outputTaskPath, outputTaskNode.ToJsonString(jso), suppressValidationErrorIfTargetPathDoesntExist: false);
}

private static void WriteTaskJsonNodeExecutionHandler(string taskPath, Config.ConfigRecord config, string fileName)
{
string outputTaskPath = Path.Combine(taskPath, fileName);
Expand Down Expand Up @@ -1425,6 +1520,13 @@ private static void UpdateVersionsForTask(string task, TaskStateStruct taskState
}
while (taskState.configTaskVersionMapping.Values.Contains(targetVersion));

if (config.abTaskReleases)
{
// In the first stage of refactoring, we keep different version numbers to retain the ability to rollback.
// In the second stage of refactoring, we are going to use the same version, which is going to significantly reduce complexity of all this.
targetVersion.Build = config.constMappingKey;
}

taskState.configTaskVersionMapping.Add(config, targetVersion);

if (!taskState.versionsUpdated.Contains(config))
Expand Down
8 changes: 8 additions & 0 deletions BuildConfigGen/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
"BuildConfigGen": {
"commandName": "Project",
"commandLineArgs": "--all-tasks --write-updates"
},
"BuildConfigGenSingle": {
"commandName": "Project",
"commandLineArgs": "--task PowerShellV2 --configs=Node20 --write-updates"
},
"BuildConfigGenSingleNew": {
"commandName": "Project",
"commandLineArgs": "--use-semver-build-config --task PowerShellV2 --configs=Node20 --write-updates"
}
}
}
Loading
Loading