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
297 lines
7 KiB
Go
297 lines
7 KiB
Go
package publish
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"log"
|
|
"log/slog"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
type Provider struct {
|
|
RootPath string
|
|
Namespace string
|
|
Provider string
|
|
DistPath string
|
|
RepoName string
|
|
Version string
|
|
GpgFingerprint string
|
|
GpgPubKeyFile string
|
|
Domain string
|
|
}
|
|
|
|
func (p *Provider) GetRoot() error {
|
|
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lines := strings.Split(string(out), "\n")
|
|
p.RootPath = lines[0]
|
|
return nil
|
|
}
|
|
|
|
func (p *Provider) CreateV1Dir() error {
|
|
// Path to semantic version dir
|
|
versionPath := p.providerDirs()
|
|
|
|
// Files to create under v1/providers/[namespace]/[provider_name]
|
|
err := p.createVersionsFile()
|
|
if err != nil {
|
|
return fmt.Errorf("[CreateV1Dir] - create versions file:%w", err)
|
|
} // Creates version file one above download, which is why downloadPath isn't used
|
|
|
|
// Files/Directories to create under v1/providers/[namespace]/[provider_name]/[version]
|
|
err = p.copyShaFiles(versionPath)
|
|
if err != nil {
|
|
return fmt.Errorf("[CreateV1Dir] - copy sha files: %w", err)
|
|
}
|
|
|
|
log.Printf("* Creating download/ in %s directory", versionPath)
|
|
downloadsPath := path.Join(versionPath, "download")
|
|
err = CreateDir(downloadsPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create darwin, freebsd, linux, windows dirs
|
|
for _, v := range [4]string{"darwin", "freebsd", "linux", "windows"} {
|
|
err = CreateDir(path.Join(downloadsPath, v))
|
|
if err != nil {
|
|
return fmt.Errorf("error creating dir '%s': %w", path.Join(downloadsPath, v), err)
|
|
}
|
|
}
|
|
|
|
// Copy all zips
|
|
err = p.copyBuildZips(downloadsPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create all individual files for build targets and each architecture for the build targets
|
|
err = p.CreateArchitectureFiles()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Provider) copyBuildZips(destPath string) error {
|
|
log.Println("* Copying build zips")
|
|
|
|
shaSums, err := p.GetShaSums()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Loop through and copy each
|
|
for _, sum := range shaSums {
|
|
zipSrcPath := path.Join(p.DistPath, sum.Path)
|
|
zipDestPath := path.Join(destPath, sum.Path)
|
|
|
|
log.Printf(" - Zip Source: %s", zipSrcPath)
|
|
log.Printf(" - Zip Dest: %s", zipDestPath)
|
|
|
|
// Copy the zip
|
|
_, err = CopyFile(zipSrcPath, zipDestPath)
|
|
if err != nil {
|
|
return fmt.Errorf("error copying file '%s': %w", zipSrcPath, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Provider) copyShaFiles(destPath string) error {
|
|
log.Printf("* Copying SHA files in %s directory", p.DistPath)
|
|
|
|
// Copy files from srcPath
|
|
shaSum := p.RepoName + "_" + p.Version + "_SHA256SUMS"
|
|
shaSumPath := path.Join(p.DistPath, shaSum)
|
|
|
|
// _SHA256SUMS file
|
|
_, err := CopyFile(shaSumPath, path.Join(destPath, shaSum))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// _SHA256SUMS.sig file
|
|
_, err = CopyFile(shaSumPath+".sig", path.Join(destPath, shaSum+".sig"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Provider) createVersionsFile() error {
|
|
log.Println("* Writing to release/v1/providers/[namespace]/[repo]/versions file")
|
|
|
|
versionPath := path.Join("release", "v1", "providers", p.Namespace, p.Provider, "versions")
|
|
|
|
shasums, err := p.GetShaSums()
|
|
if err != nil {
|
|
return fmt.Errorf("error getting sha sums: %w", err)
|
|
}
|
|
|
|
// Build the versions file...
|
|
version := Version{
|
|
Version: p.Version,
|
|
Protocols: []string{"5.1", "6.1"},
|
|
Platforms: nil,
|
|
}
|
|
for _, sum := range shasums {
|
|
// get os and arch from filename
|
|
removeFileExtension := strings.Split(sum.Path, ".zip")
|
|
if len(removeFileExtension) < 1 {
|
|
log.Fatalf("error: %s does not have .zip extension", sum.Path)
|
|
}
|
|
fileNameSplit := strings.Split(removeFileExtension[0], "_")
|
|
if len(fileNameSplit) < 4 {
|
|
log.Fatalf("filename does not match our regex: %s", removeFileExtension[0])
|
|
}
|
|
|
|
// Get build target and architecture from the zip file name
|
|
target := fileNameSplit[2]
|
|
arch := fileNameSplit[3]
|
|
|
|
version.Platforms = append(
|
|
version.Platforms, Platform{
|
|
OS: target,
|
|
Arch: arch,
|
|
},
|
|
)
|
|
}
|
|
|
|
data := Data{}
|
|
|
|
downloadPath := path.Join(p.Domain, "v1", "providers", p.Namespace, p.Provider, "versions")
|
|
err = data.LoadFromUrl(fmt.Sprintf("https://%s", downloadPath))
|
|
if err != nil {
|
|
slog.Warn("error getting existing versions file, start with empty")
|
|
// TODO: create flag for first use or make it more robust
|
|
// return fmt.Errorf("error getting existing versions file: %w", err)
|
|
}
|
|
|
|
err = data.AddVersion(version)
|
|
if err != nil {
|
|
return fmt.Errorf("error appending version: %w", err)
|
|
}
|
|
|
|
err = data.WriteToFile(versionPath)
|
|
if err != nil {
|
|
return fmt.Errorf("error saving file '%s':%w", versionPath, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Provider) providerDirs() string {
|
|
log.Println("* Creating release/v1/providers/[namespace]/[provider]/[version] directories")
|
|
|
|
target := path.Join("release", "v1", "providers", p.Namespace, p.Provider, p.Version)
|
|
|
|
err := CreateDir(target)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return target
|
|
}
|
|
|
|
func (p *Provider) CreateWellKnown() error {
|
|
log.Println("* Creating .well-known directory")
|
|
pathString := path.Join(p.RootPath, "release", ".well-known")
|
|
|
|
//nolint:gosec // this file is not sensitive, so we can use ModePerm
|
|
err := os.MkdirAll(pathString, os.ModePerm)
|
|
if err != nil && !errors.Is(err, fs.ErrExist) {
|
|
return fmt.Errorf("error creating '%s' dir: %w", pathString, err)
|
|
}
|
|
|
|
log.Println(" - Writing to .well-known/terraform.json file")
|
|
|
|
//nolint:gosec // this file is not sensitive, so we can use 0644
|
|
err = os.WriteFile(
|
|
fmt.Sprintf("%s/terraform.json", pathString),
|
|
[]byte(`{"providers.v1": "/v1/providers/"}`),
|
|
0o644,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func CreateDir(pathValue string) error {
|
|
log.Printf("* Creating %s directory", pathValue)
|
|
//nolint:gosec // this file is not sensitive, so we can use ModePerm
|
|
err := os.MkdirAll(pathValue, os.ModePerm)
|
|
if errors.Is(err, fs.ErrExist) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func ReadFile(filePath string) ([]string, error) {
|
|
rFile, err := os.Open(filePath)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fileScanner := bufio.NewScanner(rFile)
|
|
fileScanner.Split(bufio.ScanLines)
|
|
var fileLines []string
|
|
|
|
for fileScanner.Scan() {
|
|
fileLines = append(fileLines, fileScanner.Text())
|
|
}
|
|
|
|
err = rFile.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return fileLines, nil
|
|
}
|
|
|
|
func CopyFile(src, dst string) (int64, error) {
|
|
sourceFileStat, err := os.Stat(src)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if !sourceFileStat.Mode().IsRegular() {
|
|
return 0, fmt.Errorf("%s is not a regular file", src)
|
|
}
|
|
|
|
source, err := os.Open(src)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer func(source *os.File) {
|
|
err := source.Close()
|
|
if err != nil {
|
|
slog.Error("error closing source file", slog.Any("err", err))
|
|
}
|
|
}(source)
|
|
|
|
destination, err := os.Create(dst)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer func(destination *os.File) {
|
|
err := destination.Close()
|
|
if err != nil {
|
|
slog.Error("error closing destination file", slog.Any("err", err))
|
|
}
|
|
}(destination)
|
|
nBytes, err := io.Copy(destination, source)
|
|
return nBytes, err
|
|
}
|