Some checks failed
CI Workflow / Check GoReleaser config (pull_request) Successful in 4s
CI Workflow / Test readiness for publishing provider (pull_request) Failing after 14s
CI Workflow / CI run build and linting (pull_request) Failing after 10s
CI Workflow / Code coverage report (pull_request) Has been skipped
CI Workflow / CI run tests (pull_request) Failing after 57s
169 lines
3.4 KiB
Go
169 lines
3.4 KiB
Go
package publish
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
)
|
|
|
|
type Version struct {
|
|
Version string `json:"version"`
|
|
Protocols []string `json:"protocols"`
|
|
Platforms []Platform `json:"platforms"`
|
|
}
|
|
|
|
type Platform struct {
|
|
OS string `json:"os" yaml:"os"`
|
|
Arch string `json:"arch" yaml:"arch"`
|
|
}
|
|
|
|
type Data struct {
|
|
Id string `json:"id,omitempty"`
|
|
Versions []Version `json:"versions"`
|
|
}
|
|
|
|
func (d *Data) WriteToFile(filePath string) error {
|
|
// TODO: make it variable
|
|
d.Id = "tfregistry.sysops.stackit.rocks/mhenselin/stackitprivatepreview"
|
|
|
|
jsonString, err := json.Marshal(d)
|
|
if err != nil {
|
|
return fmt.Errorf("error encoding data: %w", err)
|
|
}
|
|
|
|
//nolint:gosec // this file is not sensitive, so we can use os.ModePerm
|
|
err = os.WriteFile(
|
|
filePath,
|
|
jsonString,
|
|
os.ModePerm,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("error writing data: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Data) AddVersion(v Version) error {
|
|
var newVersions []Version
|
|
for _, ver := range d.Versions {
|
|
if ver.Version != v.Version {
|
|
newVersions = append(newVersions, ver)
|
|
}
|
|
}
|
|
newVersions = append(newVersions, v)
|
|
d.Versions = newVersions
|
|
return nil
|
|
}
|
|
|
|
func (d *Data) Validate() error {
|
|
for _, v := range d.Versions {
|
|
err := v.Validate()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Data) LoadFromFile(filePath string) error {
|
|
plan, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = json.Unmarshal(plan, &d)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Data) LoadFromUrl(uri string) error {
|
|
u, err := url.ParseRequestURI(uri)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
file, err := os.CreateTemp("", "versions.*.json")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func(name string) {
|
|
//nolint:gosec // The file path is generated by os.CreateTemp and is not user-controllable
|
|
err := os.Remove(name)
|
|
if err != nil {
|
|
slog.Error("failed to remove temporary file", slog.Any("err", err))
|
|
}
|
|
}(file.Name()) // Clean up
|
|
|
|
err = DownloadFile(
|
|
u.String(),
|
|
file.Name(),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return d.LoadFromFile(file.Name())
|
|
}
|
|
|
|
func (v *Version) Validate() error {
|
|
slog.Warn("validation needs to be implemented")
|
|
return nil
|
|
}
|
|
|
|
func (v *Version) AddPlatform(p Platform) error {
|
|
if p.OS == "" || p.Arch == "" {
|
|
return fmt.Errorf("OS and Arch MUST NOT be empty")
|
|
}
|
|
v.Platforms = append(v.Platforms, p)
|
|
return nil
|
|
}
|
|
|
|
func (v *Version) AddProtocol(p string) error {
|
|
if p == "" {
|
|
return fmt.Errorf("protocol MUST NOT be empty")
|
|
}
|
|
v.Protocols = append(v.Protocols, p)
|
|
return nil
|
|
}
|
|
|
|
// DownloadFile will download a url and store it in local filepath.
|
|
// It writes to the destination file as it downloads it, without
|
|
// loading the entire file into memory.
|
|
func DownloadFile(urlValue, filepath string) error {
|
|
// Create the file
|
|
//nolint:gosec // path traversal is not a concern here, as the filepath is generated by us and not user input
|
|
out, err := os.Create(filepath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func(out *os.File) {
|
|
err := out.Close()
|
|
if err != nil {
|
|
slog.Error("failed to close file", slog.Any("err", err))
|
|
}
|
|
}(out)
|
|
|
|
// Get the data
|
|
|
|
//nolint:gosec,bodyclose // this is a controlled URL, not user input
|
|
resp, err := http.Get(urlValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func(Body io.ReadCloser) {
|
|
_ = Body.Close()
|
|
}(resp.Body)
|
|
|
|
// Write the body to file
|
|
_, err = io.Copy(out, resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|