Added a tool to update provider_meta/module_name, Terraform version and provider versions on batch basis.
Also includes a pipeline to manually release new versions and update all module names.
This commit is contained in:
parent
93eaa2ee32
commit
b28b379a7b
|
@ -0,0 +1,65 @@
|
|||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# 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.
|
||||
|
||||
name: |
|
||||
Create a new release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Release version"
|
||||
required: true
|
||||
changelog:
|
||||
description: "I have updated the CHANGELOG"
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: "Release new version"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: "Validate input"
|
||||
run: |
|
||||
[[ "${{ github.event.inputs.changelog }}" != "true" ]] && { echo 'You didn''t update the changelog.' ; exit 1; }
|
||||
[[ -n "${{ github.event.inputs.version }}" ]] || { echo 'Version not specified!'; exit 1; }
|
||||
[[ "${{ github.event.inputs.version }}" != v* ]] && { echo 'Version does not start with v!' ; exit 1; }
|
||||
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: "Update all module names"
|
||||
run: |
|
||||
cd tools/tfeditor
|
||||
go build .
|
||||
./tfeditor -path ../.. -module-name "google-pso-tool/cloud-foundation-fabric/{{ .Module }}/${{ github.event.inputs.version }}"
|
||||
cd ../..
|
||||
|
||||
git config --global user.name "Release Automation"
|
||||
git config --global user.email "cloud-foundation-fabric@google.com"
|
||||
|
||||
git commit -a -m "Release version ${{ github.event.inputs.version }}"
|
||||
git push origin master
|
||||
|
||||
- name: "Tag and release"
|
||||
run: |
|
||||
git tag ${{ github.event.inputs.version }}
|
||||
git push origin ${{ github.event.inputs.version }}
|
|
@ -0,0 +1,8 @@
|
|||
module github.com/GoogleCloudPlatform/cloud-foundation-fabric/tools/tfeditor
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/hashicorp/hcl/v2 v2.12.0 // indirect
|
||||
github.com/zclconf/go-cty v1.8.0 // indirect
|
||||
)
|
|
@ -0,0 +1,51 @@
|
|||
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/hashicorp/hcl/v2 v2.12.0 h1:PsYxySWpMD4KPaoJLnsHwtK5Qptvj/4Q6s0t4sUxZf4=
|
||||
github.com/hashicorp/hcl/v2 v2.12.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
|
||||
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
Copyright 2022 Google LLC
|
||||
|
||||
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.
|
||||
|
||||
This tool updates in-place versions.tf files to change module_name parameter
|
||||
on an automated basis. In retains all existing comments and structures.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
Source string
|
||||
Version string
|
||||
}
|
||||
|
||||
func main() {
|
||||
var path string
|
||||
var pattern string
|
||||
var moduleName string
|
||||
var terraformVersion string
|
||||
var providerVersions string
|
||||
var updateProviderVersions bool = false
|
||||
var updateTerraformVersion bool = false
|
||||
var updateModuleName bool = false
|
||||
|
||||
flag.StringVar(&path, "path", "./", "Path to search for file pattern (default ./")
|
||||
flag.StringVar(&pattern, "pattern", "versions.tf", "Pattern to search (default versions.tf)")
|
||||
flag.StringVar(&moduleName, "module-name", "", "module_name attribute for provider_meta (can be templated with {{ .Module }})")
|
||||
flag.StringVar(&providerVersions, "provider-versions", "", "set provider versions (usage: hashicorp/google>= 4.0.0,hashicorp/googlegoogle-beta== 4.0.0)")
|
||||
flag.StringVar(&terraformVersion, "terraform-version", "", "Update terraform required_version")
|
||||
flag.Parse()
|
||||
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path = fmt.Sprintf("%s/", path)
|
||||
}
|
||||
|
||||
if moduleName != "" {
|
||||
updateModuleName = true
|
||||
log.Printf("Updating module_name to: %s", moduleName)
|
||||
}
|
||||
|
||||
if terraformVersion != "" {
|
||||
updateTerraformVersion = true
|
||||
log.Printf("Updating Terraform version to: %s", terraformVersion)
|
||||
}
|
||||
|
||||
providerVersionsMap := map[string]Provider{}
|
||||
if providerVersions != "" {
|
||||
for _, v := range strings.Split(providerVersions, ",") {
|
||||
re := regexp.MustCompile("([a-zA-Z_/-]+)(.+)")
|
||||
split := re.FindAllStringSubmatch(v, -1)
|
||||
for _, splitN := range split {
|
||||
providerKey := filepath.Base(splitN[1])
|
||||
providerVersionsMap[providerKey] = Provider{Source: splitN[1], Version: splitN[2]}
|
||||
log.Printf("Updating provider %s to: %s %s", providerKey, providerVersionsMap[providerKey].Source, providerVersionsMap[providerKey].Version)
|
||||
}
|
||||
}
|
||||
updateProviderVersions = true
|
||||
}
|
||||
|
||||
log.Printf("Looking for files (%s) in: %s", pattern, path)
|
||||
|
||||
var foundFiles []string
|
||||
err := filepath.Walk(path, func(file string, f os.FileInfo, err error) error {
|
||||
if isMatch, _ := filepath.Match(pattern, filepath.Base(file)); isMatch {
|
||||
foundFiles = append(foundFiles, file)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
log.Printf("Found %d files.", len(foundFiles))
|
||||
|
||||
for _, foundFile := range foundFiles {
|
||||
fileBytes, fErr := ioutil.ReadFile(foundFile)
|
||||
if fErr != nil {
|
||||
panic(fErr)
|
||||
}
|
||||
fileBasename := filepath.Base(foundFile)
|
||||
|
||||
hclFile, diag := hclwrite.ParseConfig(fileBytes, fileBasename, hcl.Pos{})
|
||||
if diag == nil {
|
||||
hclBody := hclFile.Body()
|
||||
for _, block := range hclBody.Blocks() {
|
||||
if block.Type() == "terraform" {
|
||||
if updateTerraformVersion {
|
||||
for k, _ := range block.Body().Attributes() {
|
||||
if k == "required_version" {
|
||||
block.Body().SetAttributeValue("required_version", cty.StringVal(terraformVersion))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasProviderMetaForGoogle := false
|
||||
hasProviderMetaForGoogleBeta := false
|
||||
|
||||
// Expand template
|
||||
tmpl, tErr := template.New("modulename").Parse(moduleName)
|
||||
if tErr != nil {
|
||||
panic(tErr)
|
||||
}
|
||||
expandedBuffer := new(bytes.Buffer)
|
||||
tErr = tmpl.Execute(expandedBuffer, map[string]string{
|
||||
"Module": filepath.Base(filepath.Dir(foundFile)),
|
||||
})
|
||||
if tErr != nil {
|
||||
panic(tErr)
|
||||
}
|
||||
expandedModuleName := expandedBuffer.String()
|
||||
|
||||
for _, tfBlock := range block.Body().Blocks() {
|
||||
if tfBlock.Type() == "required_providers" && updateProviderVersions {
|
||||
for k, _ := range tfBlock.Body().Attributes() {
|
||||
if provider, ok := providerVersionsMap[k]; ok {
|
||||
tfBlock.Body().SetAttributeValue(k, cty.ObjectVal(map[string]cty.Value{
|
||||
"source": cty.StringVal(provider.Source),
|
||||
"version": cty.StringVal(provider.Version),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tfBlock.Type() == "provider_meta" && updateModuleName {
|
||||
labels := tfBlock.Labels()
|
||||
if len(labels) > 0 {
|
||||
if labels[0] == "google" {
|
||||
hasProviderMetaForGoogle = true
|
||||
tfBlock.Body().SetAttributeValue("module_name", cty.StringVal(expandedModuleName))
|
||||
}
|
||||
if labels[0] == "google-beta" {
|
||||
hasProviderMetaForGoogleBeta = true
|
||||
tfBlock.Body().SetAttributeValue("module_name", cty.StringVal(expandedModuleName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if updateModuleName {
|
||||
if !hasProviderMetaForGoogle {
|
||||
providerMetaGoogleBlock := hclwrite.NewBlock("provider_meta", []string{"google"})
|
||||
providerMetaGoogleBlock.Body().SetAttributeValue("module_name", cty.StringVal(expandedModuleName))
|
||||
block.Body().AppendBlock(providerMetaGoogleBlock)
|
||||
}
|
||||
if !hasProviderMetaForGoogleBeta {
|
||||
providerMetaGoogleBlock := hclwrite.NewBlock("provider_meta", []string{"google-beta"})
|
||||
providerMetaGoogleBlock.Body().SetAttributeValue("module_name", cty.StringVal(expandedModuleName))
|
||||
block.Body().AppendBlock(providerMetaGoogleBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Updating: %s", foundFile)
|
||||
info, sErr := os.Stat(foundFile)
|
||||
if sErr != nil {
|
||||
panic(sErr)
|
||||
}
|
||||
tempFilePath := fmt.Sprintf("%s.tmp", foundFile)
|
||||
wErr := os.WriteFile(tempFilePath, hclFile.Bytes(), info.Mode())
|
||||
if wErr != nil {
|
||||
panic(wErr)
|
||||
}
|
||||
os.Rename(tempFilePath, foundFile)
|
||||
} else {
|
||||
panic(diag)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Printf("All done.")
|
||||
}
|
Loading…
Reference in New Issue