mirror of https://github.com/harness/drone.git
140 lines
3.1 KiB
Go
140 lines
3.1 KiB
Go
// Copyright 2023 Harness, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package pypi
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
"golang.org/x/net/html"
|
|
)
|
|
|
|
var (
|
|
extensions = []string{
|
|
".tar.gz",
|
|
".tar.bz2",
|
|
".tar.xz",
|
|
".zip",
|
|
".whl",
|
|
".egg",
|
|
|
|
".exe",
|
|
".app",
|
|
".dmg",
|
|
}
|
|
|
|
exeRegex = regexp.MustCompile(`(\d+(?:\.\d+)+)`)
|
|
)
|
|
|
|
type SimpleMetadata struct {
|
|
Title string
|
|
MetaName string
|
|
Content string
|
|
Packages []Package
|
|
}
|
|
|
|
type Package struct {
|
|
SimpleURL string
|
|
Name string
|
|
ATags map[string]string
|
|
}
|
|
|
|
// URL returns the "href" attribute from the package's map.
|
|
func (p Package) URL() string {
|
|
href := p.ATags["href"]
|
|
parsedURL, err := url.Parse(href)
|
|
if err != nil {
|
|
return href
|
|
}
|
|
|
|
if parsedURL.IsAbs() {
|
|
return href
|
|
}
|
|
|
|
// If href is relative, resolve it against SimpleURL
|
|
baseURL, err := url.Parse(p.SimpleURL)
|
|
if err != nil {
|
|
log.Err(err).Msgf("failed to parse url %s", p.SimpleURL)
|
|
return href
|
|
}
|
|
|
|
return baseURL.ResolveReference(parsedURL).String()
|
|
}
|
|
|
|
func (p Package) Valid() bool {
|
|
return p.URL() != "" && p.Name != ""
|
|
}
|
|
|
|
// RequiresPython returns the "data-requires-python" attribute (unescaped) from the package's map.
|
|
func (p Package) RequiresPython() string {
|
|
val := p.ATags["data-requires-python"]
|
|
// unescape HTML entities like ">"
|
|
return html.UnescapeString(val)
|
|
}
|
|
|
|
// Version Fetches version from format:
|
|
// The wheel filename is {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
|
|
// SRC: https://packaging.python.org/en/latest/specifications/binary-distribution-format/#file-name-convention
|
|
func (p Package) Version() string {
|
|
return GetPyPIVersion(p.Name)
|
|
}
|
|
|
|
func (p Package) String() string {
|
|
return fmt.Sprintf("Name: %s, Version: %s, URL: %s, RequiresPython: %s", p.Name, p.Version(), p.URL(),
|
|
p.RequiresPython())
|
|
}
|
|
|
|
func GetPyPIVersion(filename string) string {
|
|
base, ext, err := stripRecognizedExtension(filename)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
splits := strings.Split(base, "-")
|
|
if len(splits) < 2 {
|
|
return ""
|
|
}
|
|
|
|
switch ext {
|
|
case ".whl", ".egg":
|
|
return splits[1]
|
|
case ".tar.gz", ".tar.bz2", ".tar.xz", ".zip", ".dmg", ".app":
|
|
return splits[len(splits)-1]
|
|
case ".exe":
|
|
match := exeRegex.FindStringSubmatch(filename)
|
|
if len(match) > 1 {
|
|
return match[1]
|
|
}
|
|
return splits[len(splits)-1]
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func stripRecognizedExtension(filename string) (string, string, error) {
|
|
for _, x := range extensions {
|
|
if strings.HasSuffix(strings.ToLower(filename), x) {
|
|
base := filename[:len(filename)-len(x)]
|
|
return base, x, nil
|
|
}
|
|
}
|
|
|
|
return "", "", errors.New("unrecognized file extension")
|
|
}
|