@@ -25,6 +25,14 @@ IHandler Create(
2525
2626 public sealed class HandlerFactory : RunnerService , IHandlerFactory
2727 {
28+ internal static bool ShouldTrackAsArm32Node20 ( bool deprecateArm32 , string preferredNodeVersion , string finalNodeVersion , string platformWarningMessage )
29+ {
30+ return deprecateArm32 &&
31+ ! string . IsNullOrEmpty ( platformWarningMessage ) &&
32+ string . Equals ( preferredNodeVersion , Constants . Runner . NodeMigration . Node24 , StringComparison . OrdinalIgnoreCase ) &&
33+ string . Equals ( finalNodeVersion , Constants . Runner . NodeMigration . Node20 , StringComparison . OrdinalIgnoreCase ) ;
34+ }
35+
2836 public IHandler Create (
2937 IExecutionContext executionContext ,
3038 Pipelines . ActionStepDefinitionReference action ,
@@ -65,19 +73,12 @@ public IHandler Create(
6573 nodeData . NodeVersion = Common . Constants . Runner . NodeMigration . Node20 ;
6674 }
6775
68- // Track Node.js 20 actions for deprecation annotation
69- if ( string . Equals ( nodeData . NodeVersion , Constants . Runner . NodeMigration . Node20 , StringComparison . InvariantCultureIgnoreCase ) )
70- {
71- bool warnOnNode20 = executionContext . Global . Variables ? . GetBoolean ( Constants . Runner . NodeMigration . WarnOnNode20Flag ) ?? false ;
72- if ( warnOnNode20 )
73- {
74- string actionName = GetActionName ( action ) ;
75- if ( ! string . IsNullOrEmpty ( actionName ) )
76- {
77- executionContext . Global . DeprecatedNode20Actions ? . Add ( actionName ) ;
78- }
79- }
80- }
76+ // Read flags early; actionName is also resolved up front for tracking after version is determined
77+ bool warnOnNode20 = executionContext . Global . Variables ? . GetBoolean ( Constants . Runner . NodeMigration . WarnOnNode20Flag ) ?? false ;
78+ bool deprecateArm32 = executionContext . Global . Variables ? . GetBoolean ( Constants . Runner . NodeMigration . DeprecateLinuxArm32Flag ) ?? false ;
79+ bool killArm32 = executionContext . Global . Variables ? . GetBoolean ( Constants . Runner . NodeMigration . KillLinuxArm32Flag ) ?? false ;
80+ string node20RemovalDate = executionContext . Global . Variables ? . Get ( Constants . Runner . NodeMigration . Node20RemovalDateVariable ) ;
81+ string actionName = GetActionName ( action ) ;
8182
8283 // Check if node20 was explicitly specified in the action
8384 // We don't modify if node24 was explicitly specified
@@ -87,7 +88,15 @@ public IHandler Create(
8788 bool requireNode24 = executionContext . Global . Variables ? . GetBoolean ( Constants . Runner . NodeMigration . RequireNode24Flag ) ?? false ;
8889
8990 var ( nodeVersion , configWarningMessage ) = NodeUtil . DetermineActionsNodeVersion ( environment , useNode24ByDefault , requireNode24 ) ;
90- var ( finalNodeVersion , platformWarningMessage ) = NodeUtil . CheckNodeVersionForLinuxArm32 ( nodeVersion ) ;
91+ var ( finalNodeVersion , platformWarningMessage ) = NodeUtil . CheckNodeVersionForLinuxArm32 ( nodeVersion , deprecateArm32 , killArm32 , node20RemovalDate ) ;
92+
93+ // ARM32 kill switch: fail the step
94+ if ( finalNodeVersion == null )
95+ {
96+ executionContext . Error ( platformWarningMessage ) ;
97+ throw new InvalidOperationException ( platformWarningMessage ) ;
98+ }
99+
91100 nodeData . NodeVersion = finalNodeVersion ;
92101
93102 if ( ! string . IsNullOrEmpty ( configWarningMessage ) )
@@ -100,6 +109,26 @@ public IHandler Create(
100109 executionContext . Warning ( platformWarningMessage ) ;
101110 }
102111
112+ // Track actions based on their final node version
113+ if ( ! string . IsNullOrEmpty ( actionName ) )
114+ {
115+ if ( string . Equals ( finalNodeVersion , Constants . Runner . NodeMigration . Node24 , StringComparison . OrdinalIgnoreCase ) )
116+ {
117+ // Action was upgraded from node20 to node24
118+ executionContext . Global . UpgradedToNode24Actions ? . Add ( actionName ) ;
119+ }
120+ else if ( ShouldTrackAsArm32Node20 ( deprecateArm32 , nodeVersion , finalNodeVersion , platformWarningMessage ) )
121+ {
122+ // Action is on node20 because ARM32 can't run node24
123+ executionContext . Global . Arm32Node20Actions ? . Add ( actionName ) ;
124+ }
125+ else if ( warnOnNode20 )
126+ {
127+ // Action is still running on node20 (general case)
128+ executionContext . Global . DeprecatedNode20Actions ? . Add ( actionName ) ;
129+ }
130+ }
131+
103132 // Show information about Node 24 migration in Phase 2
104133 if ( useNode24ByDefault && ! requireNode24 && string . Equals ( finalNodeVersion , Constants . Runner . NodeMigration . Node24 , StringComparison . OrdinalIgnoreCase ) )
105134 {
@@ -109,6 +138,30 @@ public IHandler Create(
109138 executionContext . Output ( infoMessage ) ;
110139 }
111140 }
141+ else if ( string . Equals ( nodeData . NodeVersion , Constants . Runner . NodeMigration . Node24 , StringComparison . InvariantCultureIgnoreCase ) )
142+ {
143+ var ( finalNodeVersion , platformWarningMessage ) = NodeUtil . CheckNodeVersionForLinuxArm32 ( nodeData . NodeVersion , deprecateArm32 , killArm32 , node20RemovalDate ) ;
144+
145+ // ARM32 kill switch: fail the step
146+ if ( finalNodeVersion == null )
147+ {
148+ executionContext . Error ( platformWarningMessage ) ;
149+ throw new InvalidOperationException ( platformWarningMessage ) ;
150+ }
151+
152+ var preferredVersion = nodeData . NodeVersion ;
153+ nodeData . NodeVersion = finalNodeVersion ;
154+
155+ if ( ! string . IsNullOrEmpty ( platformWarningMessage ) )
156+ {
157+ executionContext . Warning ( platformWarningMessage ) ;
158+ }
159+
160+ if ( ! string . IsNullOrEmpty ( actionName ) && ShouldTrackAsArm32Node20 ( deprecateArm32 , preferredVersion , finalNodeVersion , platformWarningMessage ) )
161+ {
162+ executionContext . Global . Arm32Node20Actions ? . Add ( actionName ) ;
163+ }
164+ }
112165
113166 ( handler as INodeScriptActionHandler ) . Data = nodeData ;
114167 }
0 commit comments