From 369e43c7cde3332ab791004892f71210fe97951f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Jun 2026 22:55:29 +0000 Subject: [PATCH 1/3] Initial plan From 6fdf3449cd7693289fb6bcd273cc48ab0db50554 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Jun 2026 23:09:49 +0000 Subject: [PATCH 2/3] Fix add parsing for long hyphenated repository names Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/add_package_manifest.go | 2 +- pkg/cli/add_wildcard_test.go | 9 ++++++++ pkg/cli/spec.go | 4 ++-- pkg/parser/github_urls.go | 24 ++++++++++++++++----- pkg/parser/github_urls_test.go | 38 +++++++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 8 deletions(-) diff --git a/pkg/cli/add_package_manifest.go b/pkg/cli/add_package_manifest.go index 2665134883d..f289c2ebfea 100644 --- a/pkg/cli/add_package_manifest.go +++ b/pkg/cli/add_package_manifest.go @@ -788,7 +788,7 @@ func parseRepositoryPackageSpec(spec string) (*RepoSpec, bool, error) { if len(slashParts) < 2 || slashParts[0] == "" || slashParts[1] == "" { return nil, false, nil } - if !parser.IsValidGitHubIdentifier(slashParts[0]) || !parser.IsValidGitHubIdentifier(slashParts[1]) { + if !parser.IsValidGitHubIdentifier(slashParts[0]) || !parser.IsValidGitHubRepositoryName(slashParts[1]) { return nil, false, nil } diff --git a/pkg/cli/add_wildcard_test.go b/pkg/cli/add_wildcard_test.go index c4d2443a103..bb4805b3179 100644 --- a/pkg/cli/add_wildcard_test.go +++ b/pkg/cli/add_wildcard_test.go @@ -66,6 +66,15 @@ func TestParseWorkflowSpecWithWildcard(t *testing.T) { expectedVer: "feature/github-agentic-workflows", expectedPath: "agentic-workflows/pr-review.md", }, + { + name: "direct_workflow_path_with_long_hyphenated_repo_name", + spec: "owner/long-repository-name-with-many-hyphens-in-it/agentic-workflows/business-deviation-tracker.md", + expectWildcard: false, + expectError: false, + expectedRepo: "owner/long-repository-name-with-many-hyphens-in-it", + expectedVer: "", + expectedPath: "agentic-workflows/business-deviation-tracker.md", + }, { name: "invalid_spec_too_few_parts", spec: "owner/*", diff --git a/pkg/cli/spec.go b/pkg/cli/spec.go index 5bec5ac7b39..d136127f524 100644 --- a/pkg/cli/spec.go +++ b/pkg/cli/spec.go @@ -206,7 +206,7 @@ func parseGitHubURL(spec string) (*WorkflowSpec, error) { } // Validate owner and repo - if !parser.IsValidGitHubIdentifier(owner) || !parser.IsValidGitHubIdentifier(repo) { + if !parser.IsValidGitHubIdentifier(owner) || !parser.IsValidGitHubRepositoryName(repo) { return nil, fmt.Errorf("invalid GitHub URL: '%s/%s' does not look like a valid GitHub repository", owner, repo) } @@ -371,7 +371,7 @@ func parseWorkflowSpec(spec string) (*WorkflowSpec, error) { } // Basic validation that owner and repo look like GitHub identifiers - if !parser.IsValidGitHubIdentifier(owner) || !parser.IsValidGitHubIdentifier(repo) { + if !parser.IsValidGitHubIdentifier(owner) || !parser.IsValidGitHubRepositoryName(repo) { return nil, fmt.Errorf("invalid workflow specification: '%s/%s' does not look like a valid GitHub repository", owner, repo) } diff --git a/pkg/parser/github_urls.go b/pkg/parser/github_urls.go index 91028652d5f..2fee9b1a397 100644 --- a/pkg/parser/github_urls.go +++ b/pkg/parser/github_urls.go @@ -352,11 +352,15 @@ func ParseRepoFileURL(fileURL string) (owner, repo, ref, filePath string, err er } } -// IsValidGitHubIdentifier checks if a string is a valid GitHub username or repository name -func IsValidGitHubIdentifier(s string) bool { - // GitHub identifiers can contain alphanumeric characters, hyphens, and underscores - // They cannot start or end with a hyphen and must be 1-39 characters long - if len(s) == 0 || len(s) > 39 { +const ( + maxGitHubOwnerIdentifierLength = 39 + maxGitHubRepositoryNameLength = 100 +) + +func isValidGitHubNameWithMaxLength(s string, maxLength int) bool { + // GitHub identifiers can contain alphanumeric characters, hyphens, and underscores. + // They cannot start or end with a hyphen. + if len(s) == 0 || len(s) > maxLength { return false } if s[0] == '-' || s[len(s)-1] == '-' { @@ -369,3 +373,13 @@ func IsValidGitHubIdentifier(s string) bool { } return true } + +// IsValidGitHubIdentifier checks if a string is a valid GitHub user or organization identifier. +func IsValidGitHubIdentifier(s string) bool { + return isValidGitHubNameWithMaxLength(s, maxGitHubOwnerIdentifierLength) +} + +// IsValidGitHubRepositoryName checks if a string is a valid GitHub repository name. +func IsValidGitHubRepositoryName(s string) bool { + return isValidGitHubNameWithMaxLength(s, maxGitHubRepositoryNameLength) +} diff --git a/pkg/parser/github_urls_test.go b/pkg/parser/github_urls_test.go index aca420146c3..de06b1b1379 100644 --- a/pkg/parser/github_urls_test.go +++ b/pkg/parser/github_urls_test.go @@ -837,6 +837,44 @@ func TestIsValidGitHubIdentifier(t *testing.T) { } } +func TestIsValidGitHubRepositoryName(t *testing.T) { + tests := []struct { + name string + input string + want bool + }{ + { + name: "Valid short name", + input: "repo", + want: true, + }, + { + name: "Valid long hyphenated name", + input: "long-repository-name-with-many-hyphens-that-exceeds-thirty-nine-characters", + want: true, + }, + { + name: "Invalid too long name", + input: "this-repository-name-is-intentionally-very-long-to-exceed-the-github-repository-name-limit-of-one-hundred-characters-total", + want: false, + }, + { + name: "Invalid starts with hyphen", + input: "-repo", + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := IsValidGitHubRepositoryName(tt.input) + if got != tt.want { + t.Errorf("IsValidGitHubRepositoryName() = %v, want %v", got, tt.want) + } + }) + } +} + // TestParseGitHubURL_AdditionalEdgeCases tests additional edge cases for comprehensive coverage func TestParseGitHubURL_AdditionalEdgeCases(t *testing.T) { tests := []struct { From e31d8ee7f7229e09523e8d326635d5edf116c72d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 8 Jun 2026 23:40:32 +0000 Subject: [PATCH 3/3] Address PR review threads with docs and regression tests Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/add_package_manifest_test.go | 6 +++++ pkg/cli/spec_github_url_test.go | 9 +++++++ pkg/parser/README.md | 3 ++- pkg/parser/spec_test.go | 38 +++++++++++++++++++++++++++- 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/pkg/cli/add_package_manifest_test.go b/pkg/cli/add_package_manifest_test.go index 322fa646ce2..872a142d91f 100644 --- a/pkg/cli/add_package_manifest_test.go +++ b/pkg/cli/add_package_manifest_test.go @@ -766,6 +766,12 @@ func TestParseRepositoryPackageSpec(t *testing.T) { wantRepoSlug: "owner/repo", wantVersion: "release/2026.05.27-rc_1", }, + { + name: "repo only package with long hyphenated repo name", + spec: "owner/this-repository-name-is-significantly-longer-than-thirty-nine", + wantOK: true, + wantRepoSlug: "owner/this-repository-name-is-significantly-longer-than-thirty-nine", + }, { name: "nested package path", spec: "owner/repo/packages/repo-assist", diff --git a/pkg/cli/spec_github_url_test.go b/pkg/cli/spec_github_url_test.go index 560edd762d9..5873eddfcfc 100644 --- a/pkg/cli/spec_github_url_test.go +++ b/pkg/cli/spec_github_url_test.go @@ -45,6 +45,15 @@ func TestParseGitHubURL(t *testing.T) { wantVersion: "v2.0.0", wantErr: false, }, + { + name: "blob URL with long hyphenated repo name", + url: "https://github.com/owner/this-repository-name-is-significantly-longer-than-thirty-nine/blob/main/workflows/release.md", + wantRepo: "owner/this-repository-name-is-significantly-longer-than-thirty-nine", + wantWorkflowPath: "workflows/release.md", + wantWorkflowName: "release", + wantVersion: "main", + wantErr: false, + }, { name: "invalid - non-github domain", url: "https://gitlab.com/owner/repo/blob/main/workflows/test.md", diff --git a/pkg/parser/README.md b/pkg/parser/README.md index c62e69203f2..c9ad34d6113 100644 --- a/pkg/parser/README.md +++ b/pkg/parser/README.md @@ -74,7 +74,8 @@ The package is designed for use both in the main CLI binary and in WebAssembly c | `ParseRunURLExtended` | `func(input string) (*GitHubURLComponents, error)` | Parses a workflow run URL (extended formats) | | `ParsePRURL` | `func(prURL string) (owner, repo string, prNumber int, err error)` | Parses a pull request URL | | `ParseRepoFileURL` | `func(fileURL string) (owner, repo, ref, filePath string, err error)` | Parses a repository file URL | -| `IsValidGitHubIdentifier` | `func(s string) bool` | Validates a GitHub username/org/repo name | +| `IsValidGitHubIdentifier` | `func(s string) bool` | Validates a GitHub username/org identifier | +| `IsValidGitHubRepositoryName` | `func(s string) bool` | Validates a GitHub repository name | | `GetGitHubHost` | `func() string` | Returns the GitHub host (supports GHES via `GH_HOST`) | | `GetGitHubHostForRepo` | `func(owner, repo string) string` | Returns the GitHub host for a specific repo | | `GetGitHubToken` | `func() (string, error)` | Returns the GitHub auth token from the environment | diff --git a/pkg/parser/spec_test.go b/pkg/parser/spec_test.go index 49b429c7593..8e347353ff1 100644 --- a/pkg/parser/spec_test.go +++ b/pkg/parser/spec_test.go @@ -391,7 +391,7 @@ func TestSpec_PublicAPI_LevenshteinDistance(t *testing.T) { // TestSpec_PublicAPI_IsValidGitHubIdentifier validates the documented behavior // of IsValidGitHubIdentifier as described in the package README.md. // -// Specification: Validates a GitHub username/org/repo name. +// Specification: Validates a GitHub username/org identifier. func TestSpec_PublicAPI_IsValidGitHubIdentifier(t *testing.T) { tests := []struct { name string @@ -423,6 +423,11 @@ func TestSpec_PublicAPI_IsValidGitHubIdentifier(t *testing.T) { input: "owner/repo", expected: false, }, + { + name: "owner name over 39 chars is invalid", + input: "this-owner-name-is-longer-than-thirty-nine", + expected: false, + }, } for _, tt := range tests { @@ -434,6 +439,37 @@ func TestSpec_PublicAPI_IsValidGitHubIdentifier(t *testing.T) { } } +// TestSpec_PublicAPI_IsValidGitHubRepositoryName validates the documented behavior +// of IsValidGitHubRepositoryName as described in the package README.md. +// +// Specification: Validates a GitHub repository name. +func TestSpec_PublicAPI_IsValidGitHubRepositoryName(t *testing.T) { + tests := []struct { + name string + input string + expected bool + }{ + { + name: "repo name over owner length and within 100 chars is valid", + input: "this-repository-name-is-significantly-longer-than-thirty-nine", + expected: true, + }, + { + name: "repo name over 100 chars is invalid", + input: "this-repository-name-is-way-too-long-because-it-exceeds-one-hundred-characters-when-you-keep-adding-more", + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := IsValidGitHubRepositoryName(tt.input) + assert.Equal(t, tt.expected, result, + "IsValidGitHubRepositoryName(%q) should match documented behavior", tt.input) + }) + } +} + // TestSpec_PublicAPI_IsMCPType validates the documented behavior of IsMCPType // as described in the package README.md. //