From 072e6110ee31c2d88d23608980f50a04753e49d3 Mon Sep 17 00:00:00 2001 From: Jean Machuca Date: Sat, 4 Jul 2026 22:11:44 +0000 Subject: [PATCH] feat: add source_repository and source_issues fields to package publish --- internal/server/handlers.go | 78 +++++++++++++++++++++++++--------- internal/server/server_test.go | 14 ++++++ internal/store/store.go | 24 ++++++----- 3 files changed, 85 insertions(+), 31 deletions(-) diff --git a/internal/server/handlers.go b/internal/server/handlers.go index 83fd255..92f1c45 100644 --- a/internal/server/handlers.go +++ b/internal/server/handlers.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" + "fmt" "io" "log" "net/http" @@ -89,6 +90,8 @@ func (s *Server) handlePublish() http.HandlerFunc { version := r.FormValue("version") description := r.FormValue("description") author := r.FormValue("author") + sourceRepository := r.FormValue("source_repository") + sourceIssues := r.FormValue("source_issues") downloadURL := r.FormValue("download_url") tagsStr := r.FormValue("tags") @@ -97,6 +100,13 @@ func (s *Server) handlePublish() http.HandlerFunc { return } + if sourceIssues != "" { + if err := checkURL(sourceIssues); err != nil { + writeJSON(w, http.StatusUnprocessableEntity, map[string]string{"error": "invalid or unreachable issues URL: " + err.Error()}) + return + } + } + var tags []string if tagsStr != "" { tags = strings.Split(tagsStr, ",") @@ -106,12 +116,14 @@ func (s *Server) handlePublish() http.HandlerFunc { } pkg := store.Package{ - Name: name, - Version: version, - Description: description, - Author: author, - DownloadURL: downloadURL, - Tags: tags, + Name: name, + Version: version, + Description: description, + Author: author, + SourceRepository: sourceRepository, + SourceIssues: sourceIssues, + DownloadURL: downloadURL, + Tags: tags, } file, _, err := r.FormFile("file") @@ -136,13 +148,15 @@ func (s *Server) handlePublish() http.HandlerFunc { } var req struct { - Name string `json:"name"` - Version string `json:"version"` - Description string `json:"description"` - Author string `json:"author"` - DownloadURL string `json:"download_url"` - SHA256 string `json:"sha256"` - Tags []string `json:"tags"` + Name string `json:"name"` + Version string `json:"version"` + Description string `json:"description"` + Author string `json:"author"` + SourceRepository string `json:"source_repository"` + SourceIssues string `json:"source_issues"` + DownloadURL string `json:"download_url"` + SHA256 string `json:"sha256"` + Tags []string `json:"tags"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON: " + err.Error()}) @@ -154,14 +168,23 @@ func (s *Server) handlePublish() http.HandlerFunc { return } + if req.SourceIssues != "" { + if err := checkURL(req.SourceIssues); err != nil { + writeJSON(w, http.StatusUnprocessableEntity, map[string]string{"error": "invalid or unreachable issues URL: " + err.Error()}) + return + } + } + pkg := store.Package{ - Name: req.Name, - Version: req.Version, - Description: req.Description, - Author: req.Author, - DownloadURL: req.DownloadURL, - SHA256: req.SHA256, - Tags: req.Tags, + Name: req.Name, + Version: req.Version, + Description: req.Description, + Author: req.Author, + SourceRepository: req.SourceRepository, + SourceIssues: req.SourceIssues, + DownloadURL: req.DownloadURL, + SHA256: req.SHA256, + Tags: req.Tags, } if err := s.config.Store.Put(pkg); err != nil { @@ -203,3 +226,18 @@ func writeJSON(w http.ResponseWriter, status int, v interface{}) { w.WriteHeader(status) _ = json.NewEncoder(w).Encode(v) } + +func checkURL(rawURL string) error { + if rawURL == "" { + return fmt.Errorf("empty URL") + } + u, err := http.Get(rawURL) + if err != nil { + return fmt.Errorf("unreachable: %w", err) + } + u.Body.Close() + if u.StatusCode != 200 { + return fmt.Errorf("HTTP %d", u.StatusCode) + } + return nil +} diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 651508e..db808d3 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -6,6 +6,7 @@ import ( "mime/multipart" "net/http" "net/http/httptest" + "os" "testing" "github.com/CognitiveOS-Project/registry-server/internal/auth" @@ -223,6 +224,9 @@ func TestPublishWithAuth(t *testing.T) { if pkg.SHA256 == "" { t.Error("expected SHA-256 checksum to be computed") } + + // Notary mode: no file stored on disk + assertFileNotExist(t, "new-patch-0.1.0.cgp") } func TestPublishJSONWithDownloadURL(t *testing.T) { @@ -258,6 +262,9 @@ func TestPublishJSONWithDownloadURL(t *testing.T) { if pkg.SHA256 != "abcdef1234567890" { t.Errorf("expected sha256, got %s", pkg.SHA256) } + + // Notary mode: no file stored on disk + assertFileNotExist(t, "json-patch-2.0.0.cgp") } func TestUnlock(t *testing.T) { @@ -310,6 +317,13 @@ func TestUnlockMissingFields(t *testing.T) { } } +func assertFileNotExist(t *testing.T, name string) { + t.Helper() + if _, err := os.Stat(name); err == nil { + t.Errorf("expected file %s not to exist on disk (notary mode)", name) + } +} + func TestCORSHeaders(t *testing.T) { srv, _ := setupTestServer(t) diff --git a/internal/store/store.go b/internal/store/store.go index e056439..d678ed7 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -8,17 +8,19 @@ import ( ) type Package struct { - Name string `json:"name"` - Version string `json:"version"` - Description string `json:"description"` - Author string `json:"author"` - Size int64 `json:"size"` - SHA256 string `json:"sha256"` - DownloadURL string `json:"download_url,omitempty"` - Downloads int64 `json:"downloads"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` - Tags []string `json:"tags"` + Name string `json:"name"` + Version string `json:"version"` + Description string `json:"description"` + Author string `json:"author"` + SourceRepository string `json:"source_repository,omitempty"` + SourceIssues string `json:"source_issues,omitempty"` + DownloadURL string `json:"download_url,omitempty"` + Size int64 `json:"size"` + SHA256 string `json:"sha256"` + Downloads int64 `json:"downloads"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Tags []string `json:"tags"` } type Store interface {