feat: use manifest API as source of truth for update feed metadata (#592)
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Replace custom field + config table cascade with manifest-only read for extension identity fields (element_name, package_type, display_name, target_version, php_minimum, description). Config table retained only for licensing fields (download_gating, key_prefix, support_url fallback). Fix client field: package/component/library/file → administrator. Remove issues_model import (custom field lookups removed).
This commit is contained in:
@@ -13,7 +13,6 @@ import (
|
||||
"time"
|
||||
|
||||
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/db"
|
||||
issues_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/issues"
|
||||
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/licenses"
|
||||
repo_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/repo"
|
||||
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
|
||||
@@ -163,7 +162,7 @@ func NormalizeChannel(ch string) string {
|
||||
}
|
||||
|
||||
// extensionMetadata holds resolved metadata for feed generation.
|
||||
// Fields are resolved with priority: custom field → config table → default.
|
||||
// Fields are resolved with priority: manifest → config table (gating only) → default.
|
||||
type extensionMetadata struct {
|
||||
Element string
|
||||
DisplayName string
|
||||
@@ -176,8 +175,9 @@ type extensionMetadata struct {
|
||||
KeyPrefix string
|
||||
}
|
||||
|
||||
// resolveExtensionMetadata loads extension metadata with cascading fallback:
|
||||
// org-level repo-scoped custom fields → update_stream_config → repo-derived defaults.
|
||||
// resolveExtensionMetadata loads extension metadata from the repo manifest API.
|
||||
// The manifest is the single source of truth for extension identity fields.
|
||||
// The config table is only used for licensing/gating fields not in the manifest.
|
||||
func resolveExtensionMetadata(ctx context.Context, repo *repo_model.Repository, cfg *licenses.UpdateStreamConfig) extensionMetadata {
|
||||
m := extensionMetadata{
|
||||
Element: strings.ToLower(repo.Name),
|
||||
@@ -186,91 +186,49 @@ func resolveExtensionMetadata(ctx context.Context, repo *repo_model.Repository,
|
||||
TargetVersion: "(5|6)\\..*",
|
||||
}
|
||||
|
||||
// Apply config table values.
|
||||
// Manifest is the source of truth for extension metadata.
|
||||
manifest, err := repo_model.GetRepoManifest(ctx, repo.ID)
|
||||
if err != nil {
|
||||
log.Error("resolveExtensionMetadata: GetRepoManifest for repo %d: %v", repo.ID, err)
|
||||
}
|
||||
if manifest != nil {
|
||||
if manifest.ElementName != "" {
|
||||
m.Element = manifest.ElementName
|
||||
}
|
||||
if manifest.PackageType != "" {
|
||||
m.ExtType = manifest.PackageType
|
||||
}
|
||||
if manifest.DisplayName != "" {
|
||||
m.DisplayName = manifest.DisplayName
|
||||
}
|
||||
if manifest.TargetVersion != "" {
|
||||
m.TargetVersion = manifest.TargetVersion
|
||||
}
|
||||
if manifest.PHPMinimum != "" {
|
||||
m.PHPMinimum = manifest.PHPMinimum
|
||||
}
|
||||
if manifest.Description != "" {
|
||||
m.Description = manifest.Description
|
||||
}
|
||||
if manifest.InfoURL != "" {
|
||||
m.SupportURL = manifest.InfoURL
|
||||
}
|
||||
}
|
||||
|
||||
// Config table: only licensing/gating fields (not in manifest).
|
||||
if cfg != nil {
|
||||
if cfg.ExtensionName != "" {
|
||||
m.Element = cfg.ExtensionName
|
||||
}
|
||||
if cfg.DisplayName != "" {
|
||||
m.DisplayName = cfg.DisplayName
|
||||
}
|
||||
if cfg.ExtensionType != "" {
|
||||
m.ExtType = cfg.ExtensionType
|
||||
}
|
||||
if cfg.TargetVersion != "" {
|
||||
m.TargetVersion = cfg.TargetVersion
|
||||
}
|
||||
if cfg.PHPMinimum != "" {
|
||||
m.PHPMinimum = cfg.PHPMinimum
|
||||
}
|
||||
if cfg.Description != "" {
|
||||
m.Description = cfg.Description
|
||||
}
|
||||
if cfg.SupportURL != "" {
|
||||
m.SupportURL = cfg.SupportURL
|
||||
}
|
||||
if cfg.DownloadGating != "" {
|
||||
m.DownloadGating = cfg.DownloadGating
|
||||
}
|
||||
if cfg.KeyPrefix != "" {
|
||||
m.KeyPrefix = cfg.KeyPrefix
|
||||
}
|
||||
}
|
||||
|
||||
// Override with custom field values (highest priority).
|
||||
fields, err := issues_model.GetCustomFieldsByOwner(ctx, repo.OwnerID, issues_model.CustomFieldScopeRepo)
|
||||
if err != nil {
|
||||
log.Error("resolveExtensionMetadata: GetCustomFieldsByOwner for repo %d: %v", repo.ID, err)
|
||||
return m
|
||||
}
|
||||
if len(fields) == 0 {
|
||||
return m
|
||||
}
|
||||
values, err := issues_model.GetCustomFieldValuesMap(ctx, repo.ID)
|
||||
if err != nil {
|
||||
log.Error("resolveExtensionMetadata: GetCustomFieldValuesMap for repo %d: %v", repo.ID, err)
|
||||
return m
|
||||
}
|
||||
if len(values) == 0 {
|
||||
return m
|
||||
}
|
||||
|
||||
// Build name → value map from field definitions + values.
|
||||
named := make(map[string]string, len(fields))
|
||||
for _, f := range fields {
|
||||
if v, ok := values[f.ID]; ok && v != "" {
|
||||
named[f.Name] = v
|
||||
// SupportURL from config as fallback if manifest.InfoURL is empty
|
||||
if m.SupportURL == "" && cfg.SupportURL != "" {
|
||||
m.SupportURL = cfg.SupportURL
|
||||
}
|
||||
}
|
||||
|
||||
if v := named["Extension Name"]; v != "" {
|
||||
m.Element = v
|
||||
}
|
||||
if v := named["Display Name"]; v != "" {
|
||||
m.DisplayName = v
|
||||
}
|
||||
if v := named["Extension Type"]; v != "" {
|
||||
m.ExtType = v
|
||||
}
|
||||
if v := named["Target Version"]; v != "" {
|
||||
m.TargetVersion = v
|
||||
}
|
||||
if v := named["PHP Minimum"]; v != "" {
|
||||
m.PHPMinimum = v
|
||||
}
|
||||
if v := named["Support URL"]; v != "" {
|
||||
m.SupportURL = v
|
||||
}
|
||||
if v := named["Description"]; v != "" {
|
||||
m.Description = v
|
||||
}
|
||||
if v := named["Download Gating"]; v != "" {
|
||||
m.DownloadGating = v
|
||||
}
|
||||
if v := named["Key Prefix"]; v != "" {
|
||||
m.KeyPrefix = v
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -422,13 +380,13 @@ func GenerateJoomlaXML(ctx context.Context, repo *repo_model.Repository, require
|
||||
infoURL = meta.SupportURL
|
||||
}
|
||||
|
||||
// Joomla <client> element: packages use client_id=0 in #__extensions,
|
||||
// so we must output <client>0</client> for Joomla to match the update
|
||||
// to the installed extension. Other types default to "site" (client_id=0)
|
||||
// or "administrator" (client_id=1).
|
||||
// Joomla <client> element: admin-side extensions use "administrator",
|
||||
// site-side extensions use "site". Packages, components, libraries,
|
||||
// and files are admin-side by default.
|
||||
client := "site"
|
||||
if extType == "package" {
|
||||
client = "0"
|
||||
switch extType {
|
||||
case "package", "component", "library", "file":
|
||||
client = "administrator"
|
||||
}
|
||||
|
||||
u := xmlUpdate{
|
||||
|
||||
Reference in New Issue
Block a user