Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/cgo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ jobs:
# Enforce selected production custom analyzers without blocking on unrelated
# legacy custom analyzer findings in tests or other analyzer families.
- name: Run custom linters
run: make golint-custom LINTER_FLAGS="-errstringmatch -panicinlibrarycode -manualmutexunlock -osexitinlibrary -rawloginlib -regexpcompileinfunction -fprintlnsprintf -strconvparseignorederror -jsonmarshalignoredeerror -uncheckedtypeassertion -fmterrorfnoverbs -tolowerequalfold -test=false"
run: make golint-custom LINTER_FLAGS="-errstringmatch -panicinlibrarycode -manualmutexunlock -osexitinlibrary -rawloginlib -regexpcompileinfunction -fprintlnsprintf -strconvparseignorederror -jsonmarshalignoredeerror -uncheckedtypeassertion -fmterrorfnoverbs -tolowerequalfold -httpnoctx -test=false"

# Ensure no action shell scripts invoke python or python3
- name: Lint action shell scripts
Expand Down
13 changes: 9 additions & 4 deletions pkg/cli/deps_outdated.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"context"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -39,7 +40,7 @@ type ProxyInfo struct {
}

// CheckOutdatedDependencies analyzes go.mod for outdated dependencies
func CheckOutdatedDependencies(verbose bool) ([]OutdatedDependency, error) {
func CheckOutdatedDependencies(ctx context.Context, verbose bool) ([]OutdatedDependency, error) {
depsOutdatedLog.Print("Starting outdated dependency check")

// Find go.mod file
Expand All @@ -63,7 +64,7 @@ func CheckOutdatedDependencies(verbose bool) ([]OutdatedDependency, error) {
// Check each dependency for updates
var outdated []OutdatedDependency
for _, dep := range deps {
latest, age, err := getLatestVersion(dep.Path, dep.Version, verbose)
latest, age, err := getLatestVersion(ctx, dep.Path, dep.Version, verbose)
if err != nil {
if verbose {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Warning: could not check %s: %v", dep.Path, err)))
Expand Down Expand Up @@ -159,14 +160,18 @@ func parseGoMod(path string) ([]DependencyInfo, error) {
}

// getLatestVersion queries the Go proxy for the latest version
func getLatestVersion(modulePath, currentVersion string, verbose bool) (string, time.Duration, error) {
func getLatestVersion(ctx context.Context, modulePath, currentVersion string, verbose bool) (string, time.Duration, error) {
depsOutdatedLog.Printf("Checking latest version for %s (current: %s)", modulePath, currentVersion)

// Query Go proxy API
url := fmt.Sprintf("https://proxy.golang.org/%s/@latest", modulePath)

client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(url)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return "", 0, err
}
resp, err := client.Do(req)
if err != nil {
return "", 0, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/deps_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func GenerateDependencyReport(ctx context.Context, verbose bool) (*DependencyRep
}

// Check for outdated dependencies (only direct)
outdated, err := CheckOutdatedDependencies(verbose)
outdated, err := CheckOutdatedDependencies(ctx, verbose)
if err != nil {
if verbose {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Warning: could not check outdated dependencies: %v", err)))
Expand Down
7 changes: 4 additions & 3 deletions pkg/cli/mcp_inspect.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"context"
"errors"
"fmt"
"maps"
Expand All @@ -26,7 +27,7 @@ var mcpInspectLog = logger.New("cli:mcp_inspect")
const mcpScriptsServerShutdownDelay = 500 * time.Millisecond

// InspectWorkflowMCP inspects MCP servers used by a workflow and lists available tools, resources, and roots
func InspectWorkflowMCP(workflowFile string, serverFilter string, toolFilter string, verbose bool, useActionsSecrets bool) error {
func InspectWorkflowMCP(ctx context.Context, workflowFile string, serverFilter string, toolFilter string, verbose bool, useActionsSecrets bool) error {
mcpInspectLog.Printf("Inspecting workflow MCP: workflow=%s, serverFilter=%s, toolFilter=%s",
workflowFile, serverFilter, toolFilter)

Expand Down Expand Up @@ -110,7 +111,7 @@ func InspectWorkflowMCP(workflowFile string, serverFilter string, toolFilter str
var mcpScriptsTmpDir string
if workflowData != nil && workflowData.MCPScripts != nil && len(workflowData.MCPScripts.Tools) > 0 {
// Start mcp-scripts server and add it to the list of MCP configs
config, serverCmd, tmpDir, err := startMCPScriptsServer(workflowData.MCPScripts, verbose)
config, serverCmd, tmpDir, err := startMCPScriptsServer(ctx, workflowData.MCPScripts, verbose)
if err != nil {
if verbose {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to start mcp-scripts server: %v", err)))
Expand Down Expand Up @@ -296,7 +297,7 @@ The command will:
return spawnMCPInspector(workflowFile, serverFilter, verbose)
}

return InspectWorkflowMCP(workflowFile, serverFilter, toolFilter, verbose, checkSecrets)
return InspectWorkflowMCP(cmd.Context(), workflowFile, serverFilter, toolFilter, verbose, checkSecrets)
},
}

Expand Down
25 changes: 20 additions & 5 deletions pkg/cli/mcp_inspect_mcp_scripts_server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"context"
"errors"
"fmt"
"net"
Expand Down Expand Up @@ -43,15 +44,25 @@ func findAvailablePort(startPort int, verbose bool) int {
}

// waitForServerReady waits for the HTTP server to be ready by polling the endpoint
func waitForServerReady(port int, timeout time.Duration, verbose bool) bool {
func waitForServerReady(ctx context.Context, port int, timeout time.Duration, verbose bool) bool {
deadline := time.Now().Add(timeout)
client := &http.Client{
Timeout: 1 * time.Second,
}
url := fmt.Sprintf("http://localhost:%d/", port)

for time.Now().Before(deadline) {
resp, err := client.Get(url)
select {
case <-ctx.Done():
return false
default:
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
mcpInspectLog.Printf("Failed to create request: %v", err)
return false
}
resp, err := client.Do(req)
Comment thread
pelikhan marked this conversation as resolved.
if err == nil {
if closeErr := resp.Body.Close(); closeErr != nil {
mcpInspectLog.Printf("Warning: failed to close response body: %v", closeErr)
Expand All @@ -61,7 +72,11 @@ func waitForServerReady(port int, timeout time.Duration, verbose bool) bool {
}
return true
}
time.Sleep(mcpScriptsServerStartupDelay)
select {
case <-ctx.Done():
return false
case <-time.After(mcpScriptsServerStartupDelay):
}
}

mcpInspectLog.Printf("Server did not become ready within timeout")
Expand Down Expand Up @@ -100,7 +115,7 @@ func startMCPScriptsHTTPServer(dir string, port int, verbose bool) (*exec.Cmd, e
}

// startMCPScriptsServer starts the mcp-scripts HTTP server and returns the MCP config
func startMCPScriptsServer(mcpScriptsConfig *workflow.MCPScriptsConfig, verbose bool) (*parser.RegistryMCPServerConfig, *exec.Cmd, string, error) {
func startMCPScriptsServer(ctx context.Context, mcpScriptsConfig *workflow.MCPScriptsConfig, verbose bool) (*parser.RegistryMCPServerConfig, *exec.Cmd, string, error) {
mcpInspectLog.Printf("Starting mcp-scripts server with %d tools", len(mcpScriptsConfig.Tools))

// Check if node is available
Expand Down Expand Up @@ -165,7 +180,7 @@ func startMCPScriptsServer(mcpScriptsConfig *workflow.MCPScriptsConfig, verbose
}

// Wait for the server to start up
if !waitForServerReady(port, 5*time.Second, verbose) {
if !waitForServerReady(ctx, port, 5*time.Second, verbose) {
if serverCmd.Process != nil {
// Kill the process and log warning if it fails
if err := serverCmd.Process.Kill(); err != nil && verbose {
Expand Down
17 changes: 11 additions & 6 deletions pkg/parser/remote_fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package parser

import (
"context"
"encoding/base64"
"encoding/json"
"errors"
Expand Down Expand Up @@ -554,15 +555,15 @@ func buildCommitLookupAPIPath(owner, repo, ref string) string {

// downloadFileViaGit downloads a file from a Git repository using git commands
// This is a fallback for when GitHub API authentication fails
func downloadFileViaGit(owner, repo, path, ref, host string) ([]byte, error) {
func downloadFileViaGit(ctx context.Context, owner, repo, path, ref, host string) ([]byte, error) {
remoteLog.Printf("Attempting git fallback for %s/%s/%s@%s", owner, repo, path, ref)

// First, try via raw.githubusercontent.com — no auth required for public repos and
// no dependency on git being installed.
// Only attempt raw URL for github.com repos (not GHE) since raw.githubusercontent.com
// only serves public GitHub content.
if host == "" || host == "github.com" {
content, rawErr := downloadFileViaRawURL(owner, repo, path, ref)
content, rawErr := downloadFileViaRawURL(ctx, owner, repo, path, ref)
if rawErr == nil {
return content, nil
}
Expand Down Expand Up @@ -601,7 +602,7 @@ func downloadFileViaGit(owner, repo, path, ref, host string) ([]byte, error) {

// downloadFileViaRawURL fetches a file using the raw.githubusercontent.com URL.
// This requires no authentication for public repositories and no git installation.
func downloadFileViaRawURL(owner, repo, filePath, ref string) ([]byte, error) {
func downloadFileViaRawURL(ctx context.Context, owner, repo, filePath, ref string) ([]byte, error) {
rawURL := fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s/%s", owner, repo, ref, filePath)
remoteLog.Printf("Attempting raw URL download: %s", rawURL)

Expand All @@ -610,7 +611,11 @@ func downloadFileViaRawURL(owner, repo, filePath, ref string) ([]byte, error) {

// #nosec G107 -- rawURL is constructed from workflow import configuration authored by
// the developer; the owner, repo, filePath, and ref are user-supplied workflow spec fields.
resp, err := rawClient.Get(rawURL)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)
if err != nil {
return nil, fmt.Errorf("raw URL request failed for %s: %w", rawURL, err)
}
resp, err := rawClient.Do(req)
if err != nil {
return nil, fmt.Errorf("raw URL request failed for %s: %w", rawURL, err)
}
Expand Down Expand Up @@ -851,7 +856,7 @@ func downloadFileFromGitHubWithDepth(owner, repo, path, ref string, symlinkDepth
if err != nil {
if gitutil.IsAuthError(err.Error()) {
remoteLog.Printf("REST client creation failed due to auth error, attempting git fallback for %s/%s/%s@%s: %v", owner, repo, path, ref, err)
content, gitErr := downloadFileViaGit(owner, repo, path, ref, host)
content, gitErr := downloadFileViaGit(context.Background(), owner, repo, path, ref, host)
if gitErr != nil {
remoteLog.Printf("Git fallback also failed for %s/%s/%s@%s: %v", owner, repo, path, ref, gitErr)
return nil, fmt.Errorf("failed to fetch file content: %w", err)
Expand All @@ -871,7 +876,7 @@ func downloadFileFromGitHubWithDepth(owner, repo, path, ref string, symlinkDepth
if err != nil {
if gitutil.IsAuthError(err.Error()) {
remoteLog.Printf("GitHub API authentication failed, attempting git fallback for %s/%s/%s@%s", owner, repo, path, ref)
content, gitErr := downloadFileViaGit(owner, repo, path, ref, host)
content, gitErr := downloadFileViaGit(context.Background(), owner, repo, path, ref, host)
if gitErr != nil {
return nil, fmt.Errorf("failed to fetch file content via GitHub API (auth error) and git fallback: API error: %w, Git error: %w", err, gitErr)
}
Expand Down
Loading