Functions/GenXdev.FileSystem/UnattendedModeHelper.cs
|
// ################################################################################
// Part of PowerShell module : GenXdev.FileSystem // Original cmdlet filename : UnattendedModeHelper.cs // Original author : René Vaessen / GenXdev // Version : 2.1.2025 // ################################################################################ // Copyright (c) René Vaessen / GenXdev // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ################################################################################ using System; using System.Linq; using System.Management.Automation; namespace GenXdev.FileSystem { /// <summary> /// <para type="synopsis">Provides methods to detect unattended or automated execution modes.</para> /// <para type="description">This static class contains utility methods for determining whether the current process is running in an unattended or automated environment. This detection is crucial for scripts and applications that need to adjust their behavior based on whether they are running interactively or in automated systems like CI/CD pipelines.</para> /// </summary> public static class UnattendedModeHelper { /// <summary> /// <para type="synopsis">Detects if the current process is running in unattended/automated mode.</para> /// <para type="description">This method analyzes various indicators to determine if the process is running in an automated environment. It checks for common CI/CD environment variables, console redirection, interactive mode flags, and pipeline execution patterns.</para> /// <para type="description">PARAMETERS</para> /// <para type="description">-CallersInvocation <InvocationInfo><br/> /// Optional: The caller's InvocationInfo for pipeline and automation detection.<br/> /// - <b>Position</b>: Named<br/> /// - <b>Default</b>: null<br/> /// </para> /// </summary> /// <param name="callersInvocation">Optional: The caller's InvocationInfo for pipeline and automation detection.</param> /// <returns>True if running in unattended/automated mode, otherwise false.</returns> public static bool IsUnattendedMode(InvocationInfo callersInvocation = null) { // Define environment variables that indicate automation environments string[] automationEnvVars = new[] { "JENKINS_URL", "GITHUB_ACTIONS", "TF_BUILD", "CI", "BUILD_ID", "RUNNER_OS", "SYSTEM_TEAMPROJECT", "TEAMCITY_VERSION", "TRAVIS", "APPVEYOR", "CIRCLECI", "GITLAB_CI", "AZURE_PIPELINES" }; // Check if any automation environment variable is set bool hasAutomationEnv = automationEnvVars.Any(envVar => !string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable(envVar))); // Return true if automation environment detected if (hasAutomationEnv) return true; // Check for console input/output redirection try { // Detect if standard input or output streams are redirected if (Console.IsInputRedirected || Console.IsOutputRedirected) return true; } catch { /* Ignore exceptions during console checks */ } // Check for non-interactive environment try { // Detect if the environment is non-interactive if (!System.Environment.UserInteractive) return true; } catch { /* Ignore exceptions during environment checks */ } // Check for absence of console window try { // Detect if console window width is zero, indicating no window if (Console.WindowWidth == 0) return true; } catch { // Assume no console window if access fails return true; } // Analyze pipeline execution if invocation information is provided if (callersInvocation != null) { // Retrieve pipeline position and length from invocation info int pipelineInfo = callersInvocation.PipelinePosition; int pipelineLength = callersInvocation.PipelineLength; // Determine if command is part of a pipeline bool isInPipeline = pipelineLength > 1; // Determine if command is not at the end of the pipeline bool isNotPipelineEnd = pipelineInfo < pipelineLength; // Determine if command originates from a script file bool isFromScript = !string.IsNullOrEmpty(callersInvocation.ScriptName); // Retrieve the command line text string commandLine = callersInvocation.Line ?? string.Empty; // Detect patterns indicative of automated command execution bool isAutomatedCommand = System.Text.RegularExpressions.Regex.IsMatch(commandLine, @"^\s*(foreach|%|\||;|&)") || System.Text.RegularExpressions.Regex.IsMatch(commandLine, @"\$?[\S_]+\s*=\s*"); // Return true if automated command pattern detected if (isAutomatedCommand) return true; // Determine if this is an interactive function call bool isInteractiveFunction = pipelineLength == 1 && string.IsNullOrEmpty(callersInvocation.ScriptName); // Return true if in pipeline but not interactive if (isInPipeline && !isInteractiveFunction) return true; // Return true if not at pipeline end and not interactive if (isNotPipelineEnd && !isInteractiveFunction) return true; // Return true if automated command and not interactive if (isAutomatedCommand && !isInteractiveFunction) return true; } // Assume interactive mode if no unattended indicators found return false; } } } |