mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 11:28:24 +00:00 
			
		
		
		
	I don't see why we have to use two versions of yaml. The difference between the two versions has nothing to do with our usage.
		
			
				
	
	
		
			222 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2021 The Gitea Authors. All rights reserved.
 | |
| // Use of this source code is governed by a MIT-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package rubygems
 | |
| 
 | |
| import (
 | |
| 	"archive/tar"
 | |
| 	"compress/gzip"
 | |
| 	"errors"
 | |
| 	"io"
 | |
| 	"regexp"
 | |
| 	"strings"
 | |
| 
 | |
| 	"code.gitea.io/gitea/modules/validation"
 | |
| 
 | |
| 	"gopkg.in/yaml.v3"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	// ErrMissingMetadataFile indicates a missing metadata.gz file
 | |
| 	ErrMissingMetadataFile = errors.New("Metadata file is missing")
 | |
| 	// ErrInvalidName indicates an invalid id in the metadata.gz file
 | |
| 	ErrInvalidName = errors.New("Metadata file contains an invalid name")
 | |
| 	// ErrInvalidVersion indicates an invalid version in the metadata.gz file
 | |
| 	ErrInvalidVersion = errors.New("Metadata file contains an invalid version")
 | |
| )
 | |
| 
 | |
| var versionMatcher = regexp.MustCompile(`\A[0-9]+(?:\.[0-9a-zA-Z]+)*(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?\z`)
 | |
| 
 | |
| // Package represents a RubyGems package
 | |
| type Package struct {
 | |
| 	Name     string
 | |
| 	Version  string
 | |
| 	Metadata *Metadata
 | |
| }
 | |
| 
 | |
| // Metadata represents the metadata of a RubyGems package
 | |
| type Metadata struct {
 | |
| 	Platform                string               `json:"platform,omitempty"`
 | |
| 	Description             string               `json:"description,omitempty"`
 | |
| 	Summary                 string               `json:"summary,omitempty"`
 | |
| 	Authors                 []string             `json:"authors,omitempty"`
 | |
| 	Licenses                []string             `json:"licenses,omitempty"`
 | |
| 	RequiredRubyVersion     []VersionRequirement `json:"required_ruby_version,omitempty"`
 | |
| 	RequiredRubygemsVersion []VersionRequirement `json:"required_rubygems_version,omitempty"`
 | |
| 	ProjectURL              string               `json:"project_url,omitempty"`
 | |
| 	RuntimeDependencies     []Dependency         `json:"runtime_dependencies,omitempty"`
 | |
| 	DevelopmentDependencies []Dependency         `json:"development_dependencies,omitempty"`
 | |
| }
 | |
| 
 | |
| // VersionRequirement represents a version restriction
 | |
| type VersionRequirement struct {
 | |
| 	Restriction string `json:"restriction"`
 | |
| 	Version     string `json:"version"`
 | |
| }
 | |
| 
 | |
| // Dependency represents a dependency of a RubyGems package
 | |
| type Dependency struct {
 | |
| 	Name    string               `json:"name"`
 | |
| 	Version []VersionRequirement `json:"version"`
 | |
| }
 | |
| 
 | |
| type gemspec struct {
 | |
| 	Name    string `yaml:"name"`
 | |
| 	Version struct {
 | |
| 		Version string `yaml:"version"`
 | |
| 	} `yaml:"version"`
 | |
| 	Platform     string        `yaml:"platform"`
 | |
| 	Authors      []string      `yaml:"authors"`
 | |
| 	Autorequire  interface{}   `yaml:"autorequire"`
 | |
| 	Bindir       string        `yaml:"bindir"`
 | |
| 	CertChain    []interface{} `yaml:"cert_chain"`
 | |
| 	Date         string        `yaml:"date"`
 | |
| 	Dependencies []struct {
 | |
| 		Name                string      `yaml:"name"`
 | |
| 		Requirement         requirement `yaml:"requirement"`
 | |
| 		Type                string      `yaml:"type"`
 | |
| 		Prerelease          bool        `yaml:"prerelease"`
 | |
| 		VersionRequirements requirement `yaml:"version_requirements"`
 | |
| 	} `yaml:"dependencies"`
 | |
| 	Description    string        `yaml:"description"`
 | |
| 	Executables    []string      `yaml:"executables"`
 | |
| 	Extensions     []interface{} `yaml:"extensions"`
 | |
| 	ExtraRdocFiles []string      `yaml:"extra_rdoc_files"`
 | |
| 	Files          []string      `yaml:"files"`
 | |
| 	Homepage       string        `yaml:"homepage"`
 | |
| 	Licenses       []string      `yaml:"licenses"`
 | |
| 	Metadata       struct {
 | |
| 		BugTrackerURI    string `yaml:"bug_tracker_uri"`
 | |
| 		ChangelogURI     string `yaml:"changelog_uri"`
 | |
| 		DocumentationURI string `yaml:"documentation_uri"`
 | |
| 		SourceCodeURI    string `yaml:"source_code_uri"`
 | |
| 	} `yaml:"metadata"`
 | |
| 	PostInstallMessage      interface{}   `yaml:"post_install_message"`
 | |
| 	RdocOptions             []interface{} `yaml:"rdoc_options"`
 | |
| 	RequirePaths            []string      `yaml:"require_paths"`
 | |
| 	RequiredRubyVersion     requirement   `yaml:"required_ruby_version"`
 | |
| 	RequiredRubygemsVersion requirement   `yaml:"required_rubygems_version"`
 | |
| 	Requirements            []interface{} `yaml:"requirements"`
 | |
| 	RubygemsVersion         string        `yaml:"rubygems_version"`
 | |
| 	SigningKey              interface{}   `yaml:"signing_key"`
 | |
| 	SpecificationVersion    int           `yaml:"specification_version"`
 | |
| 	Summary                 string        `yaml:"summary"`
 | |
| 	TestFiles               []interface{} `yaml:"test_files"`
 | |
| }
 | |
| 
 | |
| type requirement struct {
 | |
| 	Requirements [][]interface{} `yaml:"requirements"`
 | |
| }
 | |
| 
 | |
| // AsVersionRequirement converts into []VersionRequirement
 | |
| func (r requirement) AsVersionRequirement() []VersionRequirement {
 | |
| 	requirements := make([]VersionRequirement, 0, len(r.Requirements))
 | |
| 	for _, req := range r.Requirements {
 | |
| 		if len(req) != 2 {
 | |
| 			continue
 | |
| 		}
 | |
| 		restriction, ok := req[0].(string)
 | |
| 		if !ok {
 | |
| 			continue
 | |
| 		}
 | |
| 		vm, ok := req[1].(map[string]interface{})
 | |
| 		if !ok {
 | |
| 			continue
 | |
| 		}
 | |
| 		versionInt, ok := vm["version"]
 | |
| 		if !ok {
 | |
| 			continue
 | |
| 		}
 | |
| 		version, ok := versionInt.(string)
 | |
| 		if !ok || version == "0" {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		requirements = append(requirements, VersionRequirement{
 | |
| 			Restriction: restriction,
 | |
| 			Version:     version,
 | |
| 		})
 | |
| 	}
 | |
| 	return requirements
 | |
| }
 | |
| 
 | |
| // ParsePackageMetaData parses the metadata of a Gem package file
 | |
| func ParsePackageMetaData(r io.Reader) (*Package, error) {
 | |
| 	archive := tar.NewReader(r)
 | |
| 	for {
 | |
| 		hdr, err := archive.Next()
 | |
| 		if err == io.EOF {
 | |
| 			break
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		if hdr.Name == "metadata.gz" {
 | |
| 			return parseMetadataFile(archive)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil, ErrMissingMetadataFile
 | |
| }
 | |
| 
 | |
| func parseMetadataFile(r io.Reader) (*Package, error) {
 | |
| 	zr, err := gzip.NewReader(r)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer zr.Close()
 | |
| 
 | |
| 	var spec gemspec
 | |
| 	if err := yaml.NewDecoder(zr).Decode(&spec); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if len(spec.Name) == 0 || strings.Contains(spec.Name, "/") {
 | |
| 		return nil, ErrInvalidName
 | |
| 	}
 | |
| 
 | |
| 	if !versionMatcher.MatchString(spec.Version.Version) {
 | |
| 		return nil, ErrInvalidVersion
 | |
| 	}
 | |
| 
 | |
| 	if !validation.IsValidURL(spec.Homepage) {
 | |
| 		spec.Homepage = ""
 | |
| 	}
 | |
| 	if !validation.IsValidURL(spec.Metadata.SourceCodeURI) {
 | |
| 		spec.Metadata.SourceCodeURI = ""
 | |
| 	}
 | |
| 
 | |
| 	m := &Metadata{
 | |
| 		Platform:                spec.Platform,
 | |
| 		Description:             spec.Description,
 | |
| 		Summary:                 spec.Summary,
 | |
| 		Authors:                 spec.Authors,
 | |
| 		Licenses:                spec.Licenses,
 | |
| 		ProjectURL:              spec.Homepage,
 | |
| 		RequiredRubyVersion:     spec.RequiredRubyVersion.AsVersionRequirement(),
 | |
| 		RequiredRubygemsVersion: spec.RequiredRubygemsVersion.AsVersionRequirement(),
 | |
| 		DevelopmentDependencies: make([]Dependency, 0, 5),
 | |
| 		RuntimeDependencies:     make([]Dependency, 0, 5),
 | |
| 	}
 | |
| 
 | |
| 	for _, gemdep := range spec.Dependencies {
 | |
| 		dep := Dependency{
 | |
| 			Name:    gemdep.Name,
 | |
| 			Version: gemdep.Requirement.AsVersionRequirement(),
 | |
| 		}
 | |
| 		if gemdep.Type == ":runtime" {
 | |
| 			m.RuntimeDependencies = append(m.RuntimeDependencies, dep)
 | |
| 		} else {
 | |
| 			m.DevelopmentDependencies = append(m.DevelopmentDependencies, dep)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return &Package{
 | |
| 		Name:     spec.Name,
 | |
| 		Version:  spec.Version.Version,
 | |
| 		Metadata: m,
 | |
| 	}, nil
 | |
| }
 |