Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 38 additions & 49 deletions cli/cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import (
"encoding/json"
"fmt"
"log"
"maps"
"os"
"os/exec"
"path/filepath"
"regexp"
"slices"
"strings"

"github.com/Masterminds/semver/v3"
Expand Down Expand Up @@ -38,11 +40,6 @@ type Alias struct {
semver.Version
}

type LastestChanges struct {
versions []*semver.Version
latestAlias *Alias
}

func (a Alias) MarshalText() ([]byte, error) {
return fmt.Appendf(nil, "%d.%d", a.Major(), a.Minor()), nil
}
Expand All @@ -58,6 +55,11 @@ func (a Alias) String() string {
return fmt.Sprintf("%d.%d", a.Major(), a.Minor())
}

type Changes struct {
newVersions []*semver.Version
aliases []Alias
}

var updateCmd = &cobra.Command{
Use: "update",
Short: "Update versions file",
Expand Down Expand Up @@ -96,11 +98,10 @@ var updateCmd = &cobra.Command{
return fmt.Errorf("invalid min-version: %w", err)
}

latestChanges, err := updateVersions(
changes, err := updateVersions(
nixPath,
token,
versionsPath,
templatesPath,
minVersion,
owner,
repo,
Expand All @@ -109,19 +110,27 @@ var updateCmd = &cobra.Command{
return fmt.Errorf("unable to update versions: %w", err)
}
var messages []string
if len(latestChanges.versions) > 0 {
if len(changes.newVersions) > 0 {
var formattedVersions []string
for _, newVersion := range latestChanges.versions {
for _, newVersion := range changes.newVersions {
formattedVersions = append(formattedVersions, newVersion.String())
}
versions := strings.Join(formattedVersions, ", ")
messages = append(messages, fmt.Sprintf("Add Terraform version(s) %s", versions))
}
if latestChanges.latestAlias != nil {
messages = append(
messages,
fmt.Sprintf("Update templates to use version %s", latestChanges.latestAlias),
)

if templatesPath != "" {
latestAlias, err := getLatestAlias(changes.aliases)
if err != nil {
return err
}

err = updateTemplatesVersions(templatesPath, latestAlias)
if err != nil {
return err
}

messages = append(messages, fmt.Sprintf("Update template to use version %s", latestAlias))
}

if len(messages) > 0 {
Expand All @@ -135,11 +144,10 @@ func updateVersions(
nixPath string,
token string,
versionsPath string,
templatesPath string,
minVersion *semver.Version,
owner string,
repo string,
) (*LastestChanges, error) {
) (*Changes, error) {
versions, err := readVersions(versionsPath)
if err != nil {
return nil, fmt.Errorf("unable to read versions: %w", err)
Expand Down Expand Up @@ -212,14 +220,9 @@ func updateVersions(
return nil, fmt.Errorf("unable to write file: %w", err)
}

latestAlias, err := updateTemplatesVersions(versions, templatesPath)
if err != nil {
return nil, fmt.Errorf("unable to update templates versions: %w", err)
}

return &LastestChanges{
versions: newVersions,
latestAlias: latestAlias,
return &Changes{
newVersions: newVersions,
aliases: slices.Collect(maps.Keys(versions.Aliases)),
}, nil
}

Expand All @@ -237,29 +240,20 @@ func getLatestAlias(aliases []Alias) (*Alias, error) {
return latestAlias, nil
}

func updateTemplatesVersions(versions *Versions, templatesPath string) (*Alias, error) {
var aliases []Alias
for alias := range versions.Aliases {
aliases = append(aliases, alias)
}

latestAlias, err := getLatestAlias(aliases)
func updateTemplatesVersions(templatesPath string, latestAlias *Alias) error {
files, err := filepath.Glob(fmt.Sprintf("%s/*/flake.nix", templatesPath))
if err != nil {
return nil, fmt.Errorf("unable to get latest version: %w", err)
return fmt.Errorf("unable to find flake.nix files: %w", err)
}

files, err := filepath.Glob(fmt.Sprintf("%s/**/flake.nix", templatesPath))
if err != nil {
return nil, fmt.Errorf("unable to find flake.nix files: %w", err)
}

updated := false
re := regexp.MustCompile(`"(\d+\.\d+(\.\d+)?)"`)
for _, file := range files {
content, err := os.ReadFile(file)
if err != nil {
return nil, fmt.Errorf("unable to read file %s: %w", file, err)
log.Printf("Unable to read file %s\n", file)
continue
}

updatedContent := re.ReplaceAllString(
string(content),
fmt.Sprintf(`"%s"`, latestAlias),
Expand All @@ -271,17 +265,12 @@ func updateTemplatesVersions(versions *Versions, templatesPath string) (*Alias,

err = os.WriteFile(file, []byte(updatedContent), 0644)
if err != nil {
return nil, fmt.Errorf("unable to write file %s: %w", file, err)
return fmt.Errorf("unable to write file %s: %w", file, err)
}
log.Printf("Updated %s to version %s\n", file, latestAlias)
updated = true
}

if updated {
return latestAlias, nil
}

return nil, nil
return nil
}

func readVersions(versionsPath string) (*Versions, error) {
Expand Down Expand Up @@ -385,7 +374,7 @@ func computeVendorHash(nixPath string, version *semver.Version) (string, error)
nixPath, "build",
"--extra-experimental-features", "nix-command flakes",
"--no-link",
fmt.Sprintf(".#\"terraform-%s\"", version.String()),
fmt.Sprintf(".#\"%s-%s\"", repo, version.String()),
)

output, err := cmd.CombinedOutput()
Expand All @@ -405,9 +394,9 @@ func computeVendorHash(nixPath string, version *semver.Version) (string, error)

func init() {
updateCmd.Flags().
StringVarP(&versionsPath, "versions", "", "versions.json", "The file to be updated")
StringVarP(&versionsPath, "versions", "", "terraform.json", "The file to be updated")
updateCmd.Flags().
StringVarP(&templatesPath, "templates-dir", "", "templates", "Directory containing templates to update versions")
StringVarP(&templatesPath, "templates-dir", "", "", "Directory containing templates to update versions")
updateCmd.Flags().
StringVarP(&minVersionStr, "min-version", "", "1.0.0", "Min release version")
updateCmd.Flags().
Expand Down
2 changes: 1 addition & 1 deletion cli/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/stackbuilders/nixpkgs-terraform/cli

go 1.22.1
go 1.23

require (
github.com/Masterminds/semver/v3 v3.2.1
Expand Down
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 29 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,29 @@
let
forAllSystems = nixpkgs.lib.genAttrs (import systems);

terraformVersions = builtins.fromJSON (builtins.readFile ./versions.json);
opentofuVersions = builtins.fromJSON (builtins.readFile ./opentofu.json);

opentofuReleases = forAllSystems (
system:
self.lib.mkReleases {
inherit system;
releases = opentofuVersions.releases;
namePrefix = "opentofu";
mkRelease = self.lib.mkOpenTofu;
}
);

opentofuAliases = forAllSystems (
system:
nixpkgs.lib.mapAttrs'
(cycle: version: {
name = "opentofu-${cycle}";
value = opentofuReleases.${system}."opentofu-${version}";
})
opentofuVersions.aliases
);

terraformVersions = builtins.fromJSON (builtins.readFile ./terraform.json);

terraformReleases = forAllSystems (
system:
Expand Down Expand Up @@ -67,13 +89,15 @@
{
packages = forAllSystems (
system:
terraformReleases.${system}
opentofuReleases.${system}
// opentofuAliases.${system}
// terraformReleases.${system}
// terraformAliases.${system}
// deprecatedReleases.${system}
// deprecatedAliases.${system}
);

checks = terraformAliases;
checks = forAllSystems (system: opentofuAliases.${system} // terraformAliases.${system});

overlays = {
default =
Expand All @@ -84,7 +108,9 @@
"\"terraform-versions\" packages are deprecated; use the prefixed \"terraform-\" packages instead"
self.packages.${prev.system};
}
// self.overlays.opentofu final prev
// self.overlays.terraform final prev;
opentofu = final: prev: opentofuReleases.${prev.system} // opentofuAliases.${prev.system};
terraform = final: prev: terraformReleases.${prev.system} // terraformAliases.${prev.system};
};

Expand Down
20 changes: 20 additions & 0 deletions lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,26 @@
];
};

mkOpenTofu =
{ system ? builtins.currentSystem
, version
, hash
, vendorHash
,
}:
let
pkgs = inputs.nixpkgs.legacyPackages.${system};
in
pkgs.opentofu.overrideAttrs {
inherit version vendorHash;
src = pkgs.fetchFromGitHub {
inherit hash;
owner = "opentofu";
repo = "opentofu";
tag = "v${version}";
};
};

mkReleases =
{ system
, releases
Expand Down
15 changes: 15 additions & 0 deletions opentofu.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"releases": {
"1.11.0": {
"hash": "sha256-NnYon8VrW8oSd2DQzXTSFmf0vh4i7tJIVFHVYdKYUGk=",
"vendorHash": "sha256-aPBP7mZ4NuPAYAOW20cQ6bWQUqk9mfiaeFUS/RQyW4I="
},
"1.11.1": {
"hash": "sha256-2mtSRszdHeHDKpIu+w/Y/P8NfoSPzPBkgQ9YgKSwrls=",
"vendorHash": "sha256-aPBP7mZ4NuPAYAOW20cQ6bWQUqk9mfiaeFUS/RQyW4I="
}
},
"aliases": {
"1.11": "1.11.1"
}
}
File renamed without changes.
Loading