diff --git a/docs/plugins/apps/apps-11.1.2.tar.xz b/docs/plugins/apps/apps-11.1.2.tar.xz new file mode 100644 index 00000000..92b81477 Binary files /dev/null and b/docs/plugins/apps/apps-11.1.2.tar.xz differ diff --git a/docs/plugins/index.json b/docs/plugins/index.json index 019db454..3a212e20 100644 --- a/docs/plugins/index.json +++ b/docs/plugins/index.json @@ -10,6 +10,12 @@ "url": "apps/apps-11.1.1.tar.xz", "sha256": "81fd3c72a76358d56c8d7cdc3bbc566c3119a334c96a9855ae75187521032e6b", "releaseDate": "2026-01-28" + }, + { + "version": "11.1.2", + "url": "apps/apps-11.1.2.tar.xz", + "sha256": "1a647a33feb45e0fe96b1772df27e4f75168b3c6e01d7c3c590a90a2bfde125d", + "releaseDate": "2026-02-04" } ] } diff --git a/internal/plugin/validation.go b/internal/plugin/validation.go index 5b338278..eee1b078 100644 --- a/internal/plugin/validation.go +++ b/internal/plugin/validation.go @@ -17,6 +17,7 @@ package plugin import ( "bytes" "encoding/json" + "errors" "fmt" "os" "os/exec" @@ -30,6 +31,10 @@ import ( // ValidatePluginScript validates that a plugin script outputs a manifest matching the expected manifest. // All fields must match exactly, including Scripts and MinCLIVersion for managed plugins. func ValidatePluginScript(pluginDir string, expectedManifest PluginManifest) error { + if err := ValidateLicense(pluginDir); err != nil { + return err + } + scriptPath, err := FindPluginScript(pluginDir, expectedManifest.Name) if err != nil { return err @@ -45,6 +50,23 @@ func ValidatePluginScript(pluginDir string, expectedManifest PluginManifest) err return validateManifests(expectedManifest, *scriptManifest) } +// ValidateLicense validates that a plugin has a LICENSE.txt file. +func ValidateLicense(pluginDir string) error { + licensePath := filepath.Join(pluginDir, "LICENSE.txt") + + if _, err := os.Stat(licensePath); err != nil { + if os.IsNotExist(err) { + return errors.New("plugin must contain LICENSE.txt file") + } + + return fmt.Errorf("failed to check for LICENSE.txt: %w", err) + } + + log.Debug("Plugin license validation passed", "path", licensePath) + + return nil +} + // validateManifests compares two manifests and returns an error if they differ. func validateManifests(expected, actual PluginManifest) error { opts := cmp.Options{ diff --git a/internal/plugin/validation_test.go b/internal/plugin/validation_test.go index 8c1e1c66..a8f621d8 100644 --- a/internal/plugin/validation_test.go +++ b/internal/plugin/validation_test.go @@ -354,3 +354,67 @@ func createScript(t *testing.T, path, content string) { err := os.WriteFile(path, []byte(content), 0o755) require.NoError(t, err) } + +func TestValidateLicense_Success(t *testing.T) { + tempDir := t.TempDir() + + licensePath := filepath.Join(tempDir, "LICENSE.txt") + err := os.WriteFile(licensePath, []byte("Apache License 2.0"), 0o644) + require.NoError(t, err) + + err = ValidateLicense(tempDir) + + assert.NoError(t, err) +} + +func TestValidateLicense_Missing(t *testing.T) { + tempDir := t.TempDir() + + err := ValidateLicense(tempDir) + + require.Error(t, err) + assert.Contains(t, err.Error(), "plugin must contain LICENSE.txt file") +} + +func TestValidatePluginScript_MissingLicense(t *testing.T) { + tempDir := t.TempDir() + + manifest := PluginManifest{ + BasicPluginManifest: BasicPluginManifest{ + Name: "test", + Version: "1.0.0", + Description: "Test plugin", + Authentication: true, + }, + } + + createTestScript(t, tempDir, manifest) + + err := ValidatePluginScript(tempDir, manifest) + + require.Error(t, err) + assert.Contains(t, err.Error(), "plugin must contain LICENSE.txt file") +} + +func TestValidatePluginScript_WithLicense(t *testing.T) { + tempDir := t.TempDir() + + manifest := PluginManifest{ + BasicPluginManifest: BasicPluginManifest{ + Name: "test", + Version: "1.0.0", + Description: "Test plugin", + Authentication: true, + }, + } + + createTestScript(t, tempDir, manifest) + + licensePath := filepath.Join(tempDir, "LICENSE.txt") + err := os.WriteFile(licensePath, []byte("Apache License 2.0"), 0o644) + require.NoError(t, err) + + err = ValidatePluginScript(tempDir, manifest) + + assert.NoError(t, err) +}