// Copyright (C) 2024 Jared Allard // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package packages import ( "fmt" "os" "github.com/jaredallard/overlay/.tools/internal/steps" "gopkg.in/yaml.v3" ) // Resolver is the resolver to use to determine if an update is available. type Resolver string // Contains the supported resolvers. const ( // GitResolver is the git resolver. GitResolver Resolver = "git" // APTResolver is a version resolver powered by an APT repository. APTResolver Resolver = "apt" ) // List contains all of the packages that should be updated. type List map[string]Package // Package is an package that should be updated by the updater. type Package struct { // Name of the ebuild. This is only set when loaded from the config. // It is a readonly field. Name string `yaml:"name,omitempty"` // Resolver to use to determine if an update is available. // Currently only "git" is supported. Resolver Resolver `yaml:"resolver"` // GitOptions is the options for the git resolver. GitOptions GitOptions `yaml:"options"` // APTOptions is the options for the APT resolver. APTOptions APTOptions `yaml:"options"` // Steps are the steps to use to update the ebuild, if not set it // defaults to a copy the existing ebuild and regenerate the manifest. Steps steps.Steps `yaml:"steps"` } // LoadPackages returns a map of packages from the provided path. func LoadPackages(path string) (List, error) { f, err := os.Open(path) if err != nil { return nil, fmt.Errorf("failed to open ebuilds file: %w", err) } defer f.Close() pkgs := make(List) if err := yaml.NewDecoder(f).Decode(&pkgs); err != nil { return nil, fmt.Errorf("failed to decode ebuilds: %w", err) } return pkgs, nil } // UnmarshalYAML unmarshals the ebuild configuration from YAML while // converting options into the appropriate type for the provided // resolver. func (p *Package) UnmarshalYAML(unmarshal func(interface{}) error) error { var raw struct { Resolver Resolver `yaml:"resolver"` Options yaml.Node `yaml:"options"` Steps steps.Steps } if err := unmarshal(&raw); err != nil { return err } p.Resolver = raw.Resolver p.Steps = raw.Steps switch p.Resolver { case GitResolver: if err := raw.Options.Decode(&p.GitOptions); err != nil { return fmt.Errorf("failed to decode git options: %w", err) } case APTResolver: if err := raw.Options.Decode(&p.APTOptions); err != nil { return fmt.Errorf("failed to decode APT options: %w", err) } default: return fmt.Errorf("unsupported resolver: %s", p.Resolver) } return nil } // UnmarshalYAML unmarshals the ebuilds from YAML while carrying the // name from the key into the ebuild name. func (l List) UnmarshalYAML(unmarshal func(interface{}) error) error { pkgs := make(map[string]Package) if err := unmarshal(&pkgs); err != nil { return err } for name, ebuild := range pkgs { ebuild.Name = name l[name] = ebuild } return nil } // GitOptions is the options for the git resolver. type GitOptions struct { // URL is the URL to the git repository. Must be a valid option to // 'git clone'. URL string `yaml:"url"` // Tags denote if tags should be used as the version source. Tags bool `yaml:"tags"` // ConsiderPreReleases denotes if pre-releases should be considered, // when a tag is used and the version is able to be parsed as a // semver. Defaults to false. ConsiderPreReleases bool `yaml:"consider_pre_releases"` } // APTOptions contains the options for the APT resolver. type APTOptions struct { // Repository is the URL of the APT repository. Should match the // following format: // deb http://archive.ubuntu.com/ubuntu/ focal main Repository string `yaml:"repository"` // Package is the name of the package to watch versions for. Package string `yaml:"package"` // StripRelease is a boolean that denotes if extra release information // (in the context of a semver) should be stripped. Defaults to true. StripRelease *bool `yaml:"strip_release"` }