Revert "docs: clarify release deployment process and enable manual deploy trigger"
This reverts commit c40374c280.
This commit is contained in:
parent
c40374c280
commit
a0bc09dbe1
21 changed files with 112 additions and 134 deletions
|
|
@ -387,6 +387,7 @@ func TestDefaultHTTPKeyFetcher(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func TestHTTPAuthHandler_Permissions(t *testing.T) {
|
||||
cfg := &config.Config{
|
||||
JWTPrivateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
|
|
@ -737,3 +738,4 @@ func TestHTTPvsDNS_PermissionDifferences(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,8 +97,8 @@ func RegisterEditEndpoints(api huma.API, registry service.RegistryService, cfg *
|
|||
|
||||
// Prevent undeleting servers - once deleted, they stay deleted
|
||||
if currentServer.Meta.Official != nil &&
|
||||
currentServer.Meta.Official.Status == model.StatusDeleted &&
|
||||
newStatus != model.StatusDeleted {
|
||||
currentServer.Meta.Official.Status == model.StatusDeleted &&
|
||||
newStatus != model.StatusDeleted {
|
||||
return nil, huma.Error400BadRequest("Cannot change status of deleted server. Deleted servers cannot be undeleted.")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -639,4 +639,4 @@ func TestEditServerEndpointEdgeCases(t *testing.T) {
|
|||
// Helper function
|
||||
func stringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ func buildPermissionErrorMessage(attemptedResource string, permissions []auth.Pe
|
|||
permissionStrs = append(permissionStrs, perm.ResourcePattern)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
errorMsg := "You do not have permission to publish this server"
|
||||
if len(permissionStrs) > 0 {
|
||||
errorMsg += ". You have permission to publish: " + strings.Join(permissionStrs, ", ")
|
||||
|
|
@ -83,6 +83,6 @@ func buildPermissionErrorMessage(attemptedResource string, permissions []auth.Pe
|
|||
errorMsg += ". You do not have any publish permissions"
|
||||
}
|
||||
errorMsg += ". Attempting to publish: " + attemptedResource
|
||||
|
||||
|
||||
return errorMsg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,9 +162,9 @@ func TestPublishIntegration(t *testing.T) {
|
|||
|
||||
t.Run("publish fails with invalid token", func(t *testing.T) {
|
||||
publishReq := apiv0.ServerJSON{
|
||||
Name: "io.github.domdomegg/test-server",
|
||||
Description: "Test server",
|
||||
Version: "1.0.0",
|
||||
Name: "io.github.domdomegg/test-server",
|
||||
Description: "Test server",
|
||||
Version: "1.0.0",
|
||||
}
|
||||
|
||||
body, err := json.Marshal(publishReq)
|
||||
|
|
@ -185,7 +185,7 @@ func TestPublishIntegration(t *testing.T) {
|
|||
publishReq := apiv0.ServerJSON{
|
||||
Name: "io.github.other/test-server",
|
||||
Description: "A test server",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.0",
|
||||
Repository: model.Repository{
|
||||
URL: "https://github.com/example/test-server",
|
||||
Source: "github",
|
||||
|
|
@ -221,7 +221,7 @@ func TestPublishIntegration(t *testing.T) {
|
|||
publishReq := apiv0.ServerJSON{
|
||||
Name: "io.github.domdomegg/airtable-mcp-server",
|
||||
Description: "A test server with MCPB package",
|
||||
Version: "1.7.2",
|
||||
Version: "1.7.2",
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeMCPB,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func TestPublishRegistryValidation(t *testing.T) {
|
|||
publishReq := apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-with-npm",
|
||||
Description: "A test server with invalid npm package reference",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.0",
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeNPM,
|
||||
|
|
@ -88,7 +88,7 @@ func TestPublishRegistryValidation(t *testing.T) {
|
|||
publishReq := apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-mcpb-validation",
|
||||
Description: "A test server with MCPB package and registry validation enabled",
|
||||
Version: "0.0.36",
|
||||
Version: "0.0.36",
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeMCPB,
|
||||
|
|
@ -138,7 +138,7 @@ func TestPublishRegistryValidation(t *testing.T) {
|
|||
publishReq := apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-multiple-packages",
|
||||
Description: "A test server with multiple packages where second fails",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.0",
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeMCPB,
|
||||
|
|
@ -189,7 +189,7 @@ func TestPublishRegistryValidation(t *testing.T) {
|
|||
publishReq := apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-first-package-fails",
|
||||
Description: "A test server where first package fails",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.0",
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeNPM,
|
||||
|
|
|
|||
|
|
@ -113,9 +113,9 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
{
|
||||
name: "invalid authorization header format",
|
||||
requestBody: apiv0.ServerJSON{
|
||||
Name: "io.github.domdomegg/test-server",
|
||||
Description: "Test server",
|
||||
Version: "1.0.0",
|
||||
Name: "io.github.domdomegg/test-server",
|
||||
Description: "Test server",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
authHeader: "InvalidFormat",
|
||||
setupRegistryService: func(_ service.RegistryService) {
|
||||
|
|
@ -129,7 +129,7 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
requestBody: apiv0.ServerJSON{
|
||||
Name: "test-server",
|
||||
Description: "A test server",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
authHeader: "Bearer invalidToken",
|
||||
setupRegistryService: func(_ service.RegistryService) {
|
||||
|
|
@ -143,7 +143,7 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
requestBody: apiv0.ServerJSON{
|
||||
Name: "io.github.other/test-server",
|
||||
Description: "A test server",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.0",
|
||||
Repository: model.Repository{
|
||||
URL: "https://github.com/example/test-server",
|
||||
Source: "github",
|
||||
|
|
@ -167,7 +167,7 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
requestBody: apiv0.ServerJSON{
|
||||
Name: "example/test-server",
|
||||
Description: "A test server",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.0",
|
||||
Repository: model.Repository{
|
||||
URL: "https://github.com/example/test-server",
|
||||
Source: "github",
|
||||
|
|
@ -185,7 +185,7 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
existingServer := apiv0.ServerJSON{
|
||||
Name: "example/test-server",
|
||||
Description: "Existing test server",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.0",
|
||||
Repository: model.Repository{
|
||||
URL: "https://github.com/example/test-server-existing",
|
||||
Source: "github",
|
||||
|
|
@ -202,7 +202,7 @@ func TestPublishEndpoint(t *testing.T) {
|
|||
requestBody: apiv0.ServerJSON{
|
||||
Name: "com.example/test-server-mcpb",
|
||||
Description: "A test server with MCPB package",
|
||||
Version: "1.0.0",
|
||||
Version: "1.0.0",
|
||||
Packages: []model.Package{
|
||||
{
|
||||
RegistryType: model.RegistryTypeMCPB,
|
||||
|
|
@ -499,7 +499,7 @@ func TestPublishEndpoint_MultipleSlashesEdgeCases(t *testing.T) {
|
|||
mux.ServeHTTP(rr, req)
|
||||
|
||||
// Assertions
|
||||
assert.Equal(t, tc.expectedStatus, rr.Code,
|
||||
assert.Equal(t, tc.expectedStatus, rr.Code,
|
||||
"%s: expected status %d, got %d", tc.description, tc.expectedStatus, rr.Code)
|
||||
|
||||
if tc.expectedStatus == http.StatusBadRequest {
|
||||
|
|
@ -508,4 +508,4 @@ func TestPublishEndpoint_MultipleSlashesEdgeCases(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +41,6 @@ type ServerVersionsInput struct {
|
|||
}
|
||||
|
||||
// RegisterServersEndpoints registers all server-related endpoints
|
||||
//
|
||||
//nolint:cyclop // Multiple endpoint registrations are inherently complex
|
||||
func RegisterServersEndpoints(api huma.API, registry service.RegistryService) {
|
||||
// List servers endpoint
|
||||
|
|
|
|||
|
|
@ -509,4 +509,4 @@ func TestServersEndpointEdgeCases(t *testing.T) {
|
|||
assert.Contains(t, []model.Status{model.StatusActive, model.StatusDeprecated, model.StatusDeleted}, server.Meta.Official.Status)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -23,12 +23,12 @@ func TrailingSlashMiddleware(next http.Handler) http.Handler {
|
|||
// Create a copy of the URL and remove the trailing slash
|
||||
newURL := *r.URL
|
||||
newURL.Path = strings.TrimSuffix(r.URL.Path, "/")
|
||||
|
||||
|
||||
// Use 308 Permanent Redirect to preserve the request method
|
||||
http.Redirect(w, r, newURL.String(), http.StatusPermanentRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ var BlockedNamespaces = []string{
|
|||
// Add blocked namespaces here, e.g.:
|
||||
// "io.github.spammer",
|
||||
// "com.evil-domain",
|
||||
}
|
||||
}
|
||||
|
|
@ -300,9 +300,9 @@ func TestJWTManager_BlockedNamespaces(t *testing.T) {
|
|||
originalBlocked := auth.BlockedNamespaces
|
||||
auth.BlockedNamespaces = []string{"io.github.spammer"}
|
||||
defer func() { auth.BlockedNamespaces = originalBlocked }()
|
||||
|
||||
|
||||
jwtManager := auth.NewJWTManager(cfg)
|
||||
|
||||
|
||||
claims := auth.JWTClaims{
|
||||
AuthMethod: auth.MethodGitHubAT,
|
||||
AuthMethodSubject: "spammer",
|
||||
|
|
@ -325,9 +325,9 @@ func TestJWTManager_BlockedNamespaces(t *testing.T) {
|
|||
originalBlocked := auth.BlockedNamespaces
|
||||
auth.BlockedNamespaces = []string{"io.github.spammer"}
|
||||
defer func() { auth.BlockedNamespaces = originalBlocked }()
|
||||
|
||||
|
||||
jwtManager := auth.NewJWTManager(cfg)
|
||||
|
||||
|
||||
claims := auth.JWTClaims{
|
||||
AuthMethod: auth.MethodGitHubAT,
|
||||
AuthMethodSubject: "gooduser",
|
||||
|
|
@ -349,9 +349,9 @@ func TestJWTManager_BlockedNamespaces(t *testing.T) {
|
|||
originalBlocked := auth.BlockedNamespaces
|
||||
auth.BlockedNamespaces = []string{"io.github.badorg"}
|
||||
defer func() { auth.BlockedNamespaces = originalBlocked }()
|
||||
|
||||
|
||||
jwtManager := auth.NewJWTManager(cfg)
|
||||
|
||||
|
||||
claims := auth.JWTClaims{
|
||||
AuthMethod: auth.MethodGitHubAT,
|
||||
AuthMethodSubject: "user",
|
||||
|
|
@ -378,9 +378,9 @@ func TestJWTManager_BlockedNamespaces(t *testing.T) {
|
|||
originalBlocked := auth.BlockedNamespaces
|
||||
auth.BlockedNamespaces = []string{"io.github.spammer"}
|
||||
defer func() { auth.BlockedNamespaces = originalBlocked }()
|
||||
|
||||
|
||||
jwtManager := auth.NewJWTManager(cfg)
|
||||
|
||||
|
||||
claims := auth.JWTClaims{
|
||||
AuthMethod: auth.MethodNone,
|
||||
AuthMethodSubject: "admin",
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ const (
|
|||
MethodHTTP Method = "http"
|
||||
// No authentication - should only be used for local development and testing
|
||||
MethodNone Method = "none"
|
||||
)
|
||||
)
|
||||
|
|
@ -7,15 +7,15 @@ import (
|
|||
// Config holds the application configuration
|
||||
// See .env.example for more documentation
|
||||
type Config struct {
|
||||
ServerAddress string `env:"SERVER_ADDRESS" envDefault:":8080"`
|
||||
DatabaseURL string `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/mcp-registry?sslmode=disable"`
|
||||
SeedFrom string `env:"SEED_FROM" envDefault:""`
|
||||
Version string `env:"VERSION" envDefault:"dev"`
|
||||
GithubClientID string `env:"GITHUB_CLIENT_ID" envDefault:""`
|
||||
GithubClientSecret string `env:"GITHUB_CLIENT_SECRET" envDefault:""`
|
||||
JWTPrivateKey string `env:"JWT_PRIVATE_KEY" envDefault:""`
|
||||
EnableAnonymousAuth bool `env:"ENABLE_ANONYMOUS_AUTH" envDefault:"false"`
|
||||
EnableRegistryValidation bool `env:"ENABLE_REGISTRY_VALIDATION" envDefault:"true"`
|
||||
ServerAddress string `env:"SERVER_ADDRESS" envDefault:":8080"`
|
||||
DatabaseURL string `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/mcp-registry?sslmode=disable"`
|
||||
SeedFrom string `env:"SEED_FROM" envDefault:""`
|
||||
Version string `env:"VERSION" envDefault:"dev"`
|
||||
GithubClientID string `env:"GITHUB_CLIENT_ID" envDefault:""`
|
||||
GithubClientSecret string `env:"GITHUB_CLIENT_SECRET" envDefault:""`
|
||||
JWTPrivateKey string `env:"JWT_PRIVATE_KEY" envDefault:""`
|
||||
EnableAnonymousAuth bool `env:"ENABLE_ANONYMOUS_AUTH" envDefault:"false"`
|
||||
EnableRegistryValidation bool `env:"ENABLE_REGISTRY_VALIDATION" envDefault:"true"`
|
||||
|
||||
// OIDC Configuration
|
||||
OIDCEnabled bool `env:"OIDC_ENABLED" envDefault:"false"`
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ func TestPostgreSQL_CreateServer(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
serverJSON *apiv0.ServerJSON
|
||||
name string
|
||||
serverJSON *apiv0.ServerJSON
|
||||
officialMeta *apiv0.RegistryExtensions
|
||||
expectError bool
|
||||
errorType error
|
||||
expectError bool
|
||||
errorType error
|
||||
}{
|
||||
{
|
||||
name: "successful server creation",
|
||||
|
|
@ -285,13 +285,13 @@ func TestPostgreSQL_ListServers(t *testing.T) {
|
|||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
filter *database.ServerFilter
|
||||
cursor string
|
||||
limit int
|
||||
expectedCount int
|
||||
expectedNames []string
|
||||
expectError bool
|
||||
name string
|
||||
filter *database.ServerFilter
|
||||
cursor string
|
||||
limit int
|
||||
expectedCount int
|
||||
expectedNames []string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "list all servers",
|
||||
|
|
@ -418,12 +418,12 @@ func TestPostgreSQL_UpdateServer(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
serverName string
|
||||
version string
|
||||
updatedServer *apiv0.ServerJSON
|
||||
expectError bool
|
||||
errorType error
|
||||
name string
|
||||
serverName string
|
||||
version string
|
||||
updatedServer *apiv0.ServerJSON
|
||||
expectError bool
|
||||
errorType error
|
||||
}{
|
||||
{
|
||||
name: "successful server update",
|
||||
|
|
@ -966,4 +966,4 @@ func boolPtr(b bool) *bool {
|
|||
|
||||
func timePtr(t time.Time) *time.Time {
|
||||
return &t
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +61,7 @@ func (s *Service) ImportFromPath(ctx context.Context, path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
|
||||
// readSeedFile reads seed data from various sources
|
||||
func readSeedFile(ctx context.Context, path string) ([]*apiv0.ServerJSON, error) {
|
||||
var data []byte
|
||||
|
|
@ -188,3 +189,4 @@ func fetchFromRegistryAPI(ctx context.Context, baseURL string) ([]*apiv0.ServerJ
|
|||
|
||||
return allRecords, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -447,14 +447,14 @@ func TestUpdateServer(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
serverName string
|
||||
version string
|
||||
updatedServer *apiv0.ServerJSON
|
||||
newStatus *string
|
||||
expectError bool
|
||||
errorMsg string
|
||||
checkResult func(*testing.T, *apiv0.ServerResponse)
|
||||
name string
|
||||
serverName string
|
||||
version string
|
||||
updatedServer *apiv0.ServerJSON
|
||||
newStatus *string
|
||||
expectError bool
|
||||
errorMsg string
|
||||
checkResult func(*testing.T, *apiv0.ServerResponse)
|
||||
}{
|
||||
{
|
||||
name: "successful server update",
|
||||
|
|
@ -766,4 +766,4 @@ func TestVersionComparison(t *testing.T) {
|
|||
// Helper functions
|
||||
func stringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
}
|
||||
|
|
@ -36,4 +36,4 @@ type RepositorySource string
|
|||
const (
|
||||
SourceGitHub RepositorySource = "github"
|
||||
SourceGitLab RepositorySource = "gitlab"
|
||||
)
|
||||
)
|
||||
|
|
@ -35,7 +35,7 @@ func HasNoSpaces(s string) bool {
|
|||
func extractTemplateVariables(url string) []string {
|
||||
re := regexp.MustCompile(`\{([^}]+)\}`)
|
||||
matches := re.FindAllStringSubmatch(url, -1)
|
||||
|
||||
|
||||
var variables []string
|
||||
for _, match := range matches {
|
||||
if len(match) > 1 {
|
||||
|
|
@ -55,16 +55,16 @@ func replaceTemplateVariables(rawURL string) string {
|
|||
"{protocol}": "http",
|
||||
"{scheme}": "http",
|
||||
}
|
||||
|
||||
|
||||
result := rawURL
|
||||
for placeholder, replacement := range templateReplacements {
|
||||
result = strings.ReplaceAll(result, placeholder, replacement)
|
||||
}
|
||||
|
||||
|
||||
// Handle any remaining {variable} patterns with generic placeholder
|
||||
re := regexp.MustCompile(`\{[^}]+\}`)
|
||||
result = re.ReplaceAllString(result, "placeholder")
|
||||
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ func replaceTemplateVariables(rawURL string) string {
|
|||
func IsValidURL(rawURL string) bool {
|
||||
// Replace template variables with placeholders for parsing
|
||||
testURL := replaceTemplateVariables(rawURL)
|
||||
|
||||
|
||||
// Parse the URL
|
||||
u, err := url.Parse(testURL)
|
||||
if err != nil {
|
||||
|
|
@ -131,19 +131,19 @@ func IsValidRemoteURL(rawURL string) bool {
|
|||
if !IsValidURL(rawURL) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// Parse the URL to check for localhost restriction
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// Reject localhost URLs for remotes (security/production concerns)
|
||||
hostname := u.Hostname()
|
||||
if hostname == "localhost" || hostname == "127.0.0.1" || strings.HasSuffix(hostname, ".localhost") {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -155,31 +155,31 @@ func IsValidTemplatedURL(rawURL string, availableVariables []string, allowTempla
|
|||
if !IsValidURL(rawURL) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// Extract template variables from URL
|
||||
templateVars := extractTemplateVariables(rawURL)
|
||||
|
||||
|
||||
// If no templates are found, it's a valid static URL
|
||||
if len(templateVars) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// If templates are not allowed (e.g., for remotes), reject URLs with templates
|
||||
if !allowTemplates {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// Validate that all template variables are available
|
||||
availableSet := make(map[string]bool)
|
||||
for _, v := range availableVariables {
|
||||
availableSet[v] = true
|
||||
}
|
||||
|
||||
|
||||
for _, templateVar := range templateVars {
|
||||
if !availableSet[templateVar] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -505,4 +505,4 @@ func isValidHostForDomain(hostname, publisherDomain string) bool {
|
|||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue