code.gitea.io/gitea@v1.19.3/modules/packages/chef/metadata.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package chef 5 6 import ( 7 "archive/tar" 8 "compress/gzip" 9 "io" 10 "regexp" 11 "strings" 12 13 "code.gitea.io/gitea/modules/json" 14 "code.gitea.io/gitea/modules/util" 15 "code.gitea.io/gitea/modules/validation" 16 ) 17 18 const ( 19 KeyBits = 4096 20 SettingPublicPem = "chef.public_pem" 21 ) 22 23 var ( 24 ErrMissingMetadataFile = util.NewInvalidArgumentErrorf("metadata.json file is missing") 25 ErrInvalidName = util.NewInvalidArgumentErrorf("package name is invalid") 26 ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid") 27 28 namePattern = regexp.MustCompile(`\A\S+\z`) 29 versionPattern = regexp.MustCompile(`\A\d+\.\d+(?:\.\d+)?\z`) 30 ) 31 32 // Package represents a Chef package 33 type Package struct { 34 Name string 35 Version string 36 Metadata *Metadata 37 } 38 39 // Metadata represents the metadata of a Chef package 40 type Metadata struct { 41 Description string `json:"description,omitempty"` 42 LongDescription string `json:"long_description,omitempty"` 43 Author string `json:"author,omitempty"` 44 License string `json:"license,omitempty"` 45 RepositoryURL string `json:"repository_url,omitempty"` 46 Dependencies map[string]string `json:"dependencies,omitempty"` 47 } 48 49 type chefMetadata struct { 50 Name string `json:"name"` 51 Description string `json:"description"` 52 LongDescription string `json:"long_description"` 53 Maintainer string `json:"maintainer"` 54 MaintainerEmail string `json:"maintainer_email"` 55 License string `json:"license"` 56 Platforms map[string]string `json:"platforms"` 57 Dependencies map[string]string `json:"dependencies"` 58 Providing map[string]string `json:"providing"` 59 Recipes map[string]string `json:"recipes"` 60 Version string `json:"version"` 61 SourceURL string `json:"source_url"` 62 IssuesURL string `json:"issues_url"` 63 Privacy bool `json:"privacy"` 64 ChefVersions [][]string `json:"chef_versions"` 65 Gems [][]string `json:"gems"` 66 EagerLoadLibraries bool `json:"eager_load_libraries"` 67 } 68 69 // ParsePackage parses the Chef package file 70 func ParsePackage(r io.Reader) (*Package, error) { 71 gzr, err := gzip.NewReader(r) 72 if err != nil { 73 return nil, err 74 } 75 defer gzr.Close() 76 77 tr := tar.NewReader(gzr) 78 for { 79 hd, err := tr.Next() 80 if err == io.EOF { 81 break 82 } 83 if err != nil { 84 return nil, err 85 } 86 87 if hd.Typeflag != tar.TypeReg { 88 continue 89 } 90 91 if strings.Count(hd.Name, "/") != 1 { 92 continue 93 } 94 95 if hd.FileInfo().Name() == "metadata.json" { 96 return ParseChefMetadata(tr) 97 } 98 } 99 100 return nil, ErrMissingMetadataFile 101 } 102 103 // ParseChefMetadata parses a metadata.json file to retrieve the metadata of a Chef package 104 func ParseChefMetadata(r io.Reader) (*Package, error) { 105 var cm chefMetadata 106 if err := json.NewDecoder(r).Decode(&cm); err != nil { 107 return nil, err 108 } 109 110 if !namePattern.MatchString(cm.Name) { 111 return nil, ErrInvalidName 112 } 113 114 if !versionPattern.MatchString(cm.Version) { 115 return nil, ErrInvalidVersion 116 } 117 118 if !validation.IsValidURL(cm.SourceURL) { 119 cm.SourceURL = "" 120 } 121 122 return &Package{ 123 Name: cm.Name, 124 Version: cm.Version, 125 Metadata: &Metadata{ 126 Description: cm.Description, 127 LongDescription: cm.LongDescription, 128 Author: cm.Maintainer, 129 License: cm.License, 130 RepositoryURL: cm.SourceURL, 131 Dependencies: cm.Dependencies, 132 }, 133 }, nil 134 }