Skip to content

Commit 55aabaf

Browse files
authored
Fix login guard to skip interactive prompts in CI/CD environments (#5528)
- Added CI/CD detection using the existing resource.IsRunningOnCI() function - Modified ensureLogin() to check for CI/CD environment before showing interactive prompt - If in CI/CD: returns authentication error immediately without blocking - If interactive: preserves existing UX with automatic login prompt - Added comprehensive tests covering multiple CI providers (GitHub Actions, Azure Pipelines, Generic CI)
1 parent e64cfe5 commit 55aabaf

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

cli/azd/cmd/middleware/login_guard.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
1010
"github.com/azure/azure-dev/cli/azd/cmd/actions"
11+
"github.com/azure/azure-dev/cli/azd/internal/tracing/resource"
1112
"github.com/azure/azure-dev/cli/azd/pkg/auth"
1213
"github.com/azure/azure-dev/cli/azd/pkg/cloud"
1314
"github.com/azure/azure-dev/cli/azd/pkg/input"
@@ -62,6 +63,11 @@ func (l *LoginGuardMiddleware) Run(ctx context.Context, next NextFn) (*actions.A
6263
func (l *LoginGuardMiddleware) ensureLogin(ctx context.Context) (azcore.TokenCredential, error) {
6364
cred, credentialErr := l.authManager.CredentialForCurrentUser(ctx, nil)
6465
if credentialErr != nil {
66+
// If running in CI/CD, don't prompt for interactive login, just return the authentication error
67+
if resource.IsRunningOnCI() {
68+
return nil, credentialErr
69+
}
70+
6571
loginWarning := output.WithWarningFormat("WARNING: You must be logged into Azure perform this action")
6672
l.console.Message(ctx, loginWarning)
6773

cli/azd/cmd/middleware/login_guard_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package middleware
55

66
import (
77
"context"
8+
"os"
89
"strings"
910
"testing"
1011

@@ -63,6 +64,59 @@ func Test_LoginGuard_Run(t *testing.T) {
6364
require.Error(t, err)
6465
require.Nil(t, result)
6566
})
67+
t.Run("NotLoggedInCI", func(t *testing.T) {
68+
// Test with different CI environment variables
69+
testCases := []struct {
70+
envVar string
71+
envValue string
72+
testName string
73+
}{
74+
{"GITHUB_ACTIONS", "true", "GitHub Actions"},
75+
{"TF_BUILD", "True", "Azure Pipelines"},
76+
{"CI", "true", "Generic CI"},
77+
}
78+
79+
for _, tc := range testCases {
80+
t.Run(tc.testName, func(t *testing.T) {
81+
// Set up CI environment variable to simulate CI/CD
82+
originalValue := os.Getenv(tc.envVar)
83+
os.Setenv(tc.envVar, tc.envValue)
84+
defer func() {
85+
if originalValue == "" {
86+
os.Unsetenv(tc.envVar)
87+
} else {
88+
os.Setenv(tc.envVar, originalValue)
89+
}
90+
}()
91+
92+
mockContext := mocks.NewMockContext(context.Background())
93+
// In CI, we should NOT get a console confirmation prompt
94+
// The test will fail if console.Confirm is called
95+
mockContext.Console.
96+
WhenConfirm(func(options input.ConsoleOptions) bool {
97+
t.Fatal("Console.Confirm should not be called in CI/CD environment")
98+
return false
99+
})
100+
101+
mockAuthManager := &mockCurrentUserAuthManager{}
102+
mockAuthManager.On("Cloud").Return(cloud.AzurePublic())
103+
mockAuthManager.
104+
On("CredentialForCurrentUser", *mockContext.Context, mock.Anything).
105+
Return(nil, auth.ErrNoCurrentUser)
106+
107+
middleware := LoginGuardMiddleware{
108+
console: mockContext.Console,
109+
authManager: mockAuthManager,
110+
workflowRunner: &workflow.Runner{},
111+
}
112+
113+
result, err := middleware.Run(*mockContext.Context, next)
114+
require.Error(t, err)
115+
require.Nil(t, result)
116+
require.Equal(t, auth.ErrNoCurrentUser, err)
117+
})
118+
}
119+
})
66120
}
67121

68122
func next(ctx context.Context) (*actions.ActionResult, error) {

0 commit comments

Comments
 (0)