Skip to content

Commit 82822a6

Browse files
authored
Merge pull request #672 from rosmo/module-attribution
Module attribution and version updater tool, plus release automation
2 parents 93eaa2e + b28b379 commit 82822a6

File tree

4 files changed

+322
-0
lines changed

4 files changed

+322
-0
lines changed

.github/workflows/release.yml

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright 2022 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: |
16+
Create a new release
17+
18+
on:
19+
workflow_dispatch:
20+
inputs:
21+
version:
22+
description: "Release version"
23+
required: true
24+
changelog:
25+
description: "I have updated the CHANGELOG"
26+
required: true
27+
type: boolean
28+
29+
permissions:
30+
contents: write
31+
32+
jobs:
33+
release:
34+
name: "Release new version"
35+
runs-on: ubuntu-latest
36+
steps:
37+
- uses: actions/checkout@v2
38+
39+
- name: "Validate input"
40+
run: |
41+
[[ "${{ github.event.inputs.changelog }}" != "true" ]] && { echo 'You didn''t update the changelog.' ; exit 1; }
42+
[[ -n "${{ github.event.inputs.version }}" ]] || { echo 'Version not specified!'; exit 1; }
43+
[[ "${{ github.event.inputs.version }}" != v* ]] && { echo 'Version does not start with v!' ; exit 1; }
44+
45+
- uses: actions/setup-go@v3
46+
with:
47+
go-version: 1.16
48+
49+
- name: "Update all module names"
50+
run: |
51+
cd tools/tfeditor
52+
go build .
53+
./tfeditor -path ../.. -module-name "google-pso-tool/cloud-foundation-fabric/{{ .Module }}/${{ github.event.inputs.version }}"
54+
cd ../..
55+
56+
git config --global user.name "Release Automation"
57+
git config --global user.email "[email protected]"
58+
59+
git commit -a -m "Release version ${{ github.event.inputs.version }}"
60+
git push origin master
61+
62+
- name: "Tag and release"
63+
run: |
64+
git tag ${{ github.event.inputs.version }}
65+
git push origin ${{ github.event.inputs.version }}

tools/tfeditor/go.mod

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module github.com/GoogleCloudPlatform/cloud-foundation-fabric/tools/tfeditor
2+
3+
go 1.16
4+
5+
require (
6+
github.com/hashicorp/hcl/v2 v2.12.0 // indirect
7+
github.com/zclconf/go-cty v1.8.0 // indirect
8+
)

tools/tfeditor/go.sum

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
2+
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
3+
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
4+
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
5+
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
6+
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
7+
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
8+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9+
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
10+
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
11+
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
12+
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
13+
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
14+
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
15+
github.com/hashicorp/hcl/v2 v2.12.0 h1:PsYxySWpMD4KPaoJLnsHwtK5Qptvj/4Q6s0t4sUxZf4=
16+
github.com/hashicorp/hcl/v2 v2.12.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
17+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
18+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
19+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
20+
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
21+
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
22+
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
23+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
24+
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
25+
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
26+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
27+
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
28+
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
29+
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
30+
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
31+
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
32+
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
33+
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
34+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
35+
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
36+
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
37+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
38+
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
39+
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
40+
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
41+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
42+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
43+
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
44+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
45+
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
46+
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
47+
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
48+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
49+
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
50+
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
51+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

tools/tfeditor/main.go

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
Copyright 2022 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
This tool updates in-place versions.tf files to change module_name parameter
17+
on an automated basis. In retains all existing comments and structures.
18+
*/
19+
package main
20+
21+
import (
22+
"bytes"
23+
"flag"
24+
"fmt"
25+
"io/ioutil"
26+
"log"
27+
"os"
28+
"path/filepath"
29+
"regexp"
30+
"strings"
31+
"text/template"
32+
33+
"github.com/hashicorp/hcl/v2"
34+
"github.com/hashicorp/hcl/v2/hclwrite"
35+
"github.com/zclconf/go-cty/cty"
36+
)
37+
38+
type Provider struct {
39+
Source string
40+
Version string
41+
}
42+
43+
func main() {
44+
var path string
45+
var pattern string
46+
var moduleName string
47+
var terraformVersion string
48+
var providerVersions string
49+
var updateProviderVersions bool = false
50+
var updateTerraformVersion bool = false
51+
var updateModuleName bool = false
52+
53+
flag.StringVar(&path, "path", "./", "Path to search for file pattern (default ./")
54+
flag.StringVar(&pattern, "pattern", "versions.tf", "Pattern to search (default versions.tf)")
55+
flag.StringVar(&moduleName, "module-name", "", "module_name attribute for provider_meta (can be templated with {{ .Module }})")
56+
flag.StringVar(&providerVersions, "provider-versions", "", "set provider versions (usage: hashicorp/google>= 4.0.0,hashicorp/googlegoogle-beta== 4.0.0)")
57+
flag.StringVar(&terraformVersion, "terraform-version", "", "Update terraform required_version")
58+
flag.Parse()
59+
60+
if !strings.HasSuffix(path, "/") {
61+
path = fmt.Sprintf("%s/", path)
62+
}
63+
64+
if moduleName != "" {
65+
updateModuleName = true
66+
log.Printf("Updating module_name to: %s", moduleName)
67+
}
68+
69+
if terraformVersion != "" {
70+
updateTerraformVersion = true
71+
log.Printf("Updating Terraform version to: %s", terraformVersion)
72+
}
73+
74+
providerVersionsMap := map[string]Provider{}
75+
if providerVersions != "" {
76+
for _, v := range strings.Split(providerVersions, ",") {
77+
re := regexp.MustCompile("([a-zA-Z_/-]+)(.+)")
78+
split := re.FindAllStringSubmatch(v, -1)
79+
for _, splitN := range split {
80+
providerKey := filepath.Base(splitN[1])
81+
providerVersionsMap[providerKey] = Provider{Source: splitN[1], Version: splitN[2]}
82+
log.Printf("Updating provider %s to: %s %s", providerKey, providerVersionsMap[providerKey].Source, providerVersionsMap[providerKey].Version)
83+
}
84+
}
85+
updateProviderVersions = true
86+
}
87+
88+
log.Printf("Looking for files (%s) in: %s", pattern, path)
89+
90+
var foundFiles []string
91+
err := filepath.Walk(path, func(file string, f os.FileInfo, err error) error {
92+
if isMatch, _ := filepath.Match(pattern, filepath.Base(file)); isMatch {
93+
foundFiles = append(foundFiles, file)
94+
}
95+
return nil
96+
})
97+
log.Printf("Found %d files.", len(foundFiles))
98+
99+
for _, foundFile := range foundFiles {
100+
fileBytes, fErr := ioutil.ReadFile(foundFile)
101+
if fErr != nil {
102+
panic(fErr)
103+
}
104+
fileBasename := filepath.Base(foundFile)
105+
106+
hclFile, diag := hclwrite.ParseConfig(fileBytes, fileBasename, hcl.Pos{})
107+
if diag == nil {
108+
hclBody := hclFile.Body()
109+
for _, block := range hclBody.Blocks() {
110+
if block.Type() == "terraform" {
111+
if updateTerraformVersion {
112+
for k, _ := range block.Body().Attributes() {
113+
if k == "required_version" {
114+
block.Body().SetAttributeValue("required_version", cty.StringVal(terraformVersion))
115+
}
116+
}
117+
}
118+
119+
hasProviderMetaForGoogle := false
120+
hasProviderMetaForGoogleBeta := false
121+
122+
// Expand template
123+
tmpl, tErr := template.New("modulename").Parse(moduleName)
124+
if tErr != nil {
125+
panic(tErr)
126+
}
127+
expandedBuffer := new(bytes.Buffer)
128+
tErr = tmpl.Execute(expandedBuffer, map[string]string{
129+
"Module": filepath.Base(filepath.Dir(foundFile)),
130+
})
131+
if tErr != nil {
132+
panic(tErr)
133+
}
134+
expandedModuleName := expandedBuffer.String()
135+
136+
for _, tfBlock := range block.Body().Blocks() {
137+
if tfBlock.Type() == "required_providers" && updateProviderVersions {
138+
for k, _ := range tfBlock.Body().Attributes() {
139+
if provider, ok := providerVersionsMap[k]; ok {
140+
tfBlock.Body().SetAttributeValue(k, cty.ObjectVal(map[string]cty.Value{
141+
"source": cty.StringVal(provider.Source),
142+
"version": cty.StringVal(provider.Version),
143+
}))
144+
}
145+
}
146+
}
147+
148+
if tfBlock.Type() == "provider_meta" && updateModuleName {
149+
labels := tfBlock.Labels()
150+
if len(labels) > 0 {
151+
if labels[0] == "google" {
152+
hasProviderMetaForGoogle = true
153+
tfBlock.Body().SetAttributeValue("module_name", cty.StringVal(expandedModuleName))
154+
}
155+
if labels[0] == "google-beta" {
156+
hasProviderMetaForGoogleBeta = true
157+
tfBlock.Body().SetAttributeValue("module_name", cty.StringVal(expandedModuleName))
158+
}
159+
}
160+
}
161+
}
162+
163+
if updateModuleName {
164+
if !hasProviderMetaForGoogle {
165+
providerMetaGoogleBlock := hclwrite.NewBlock("provider_meta", []string{"google"})
166+
providerMetaGoogleBlock.Body().SetAttributeValue("module_name", cty.StringVal(expandedModuleName))
167+
block.Body().AppendBlock(providerMetaGoogleBlock)
168+
}
169+
if !hasProviderMetaForGoogleBeta {
170+
providerMetaGoogleBlock := hclwrite.NewBlock("provider_meta", []string{"google-beta"})
171+
providerMetaGoogleBlock.Body().SetAttributeValue("module_name", cty.StringVal(expandedModuleName))
172+
block.Body().AppendBlock(providerMetaGoogleBlock)
173+
}
174+
}
175+
}
176+
}
177+
178+
log.Printf("Updating: %s", foundFile)
179+
info, sErr := os.Stat(foundFile)
180+
if sErr != nil {
181+
panic(sErr)
182+
}
183+
tempFilePath := fmt.Sprintf("%s.tmp", foundFile)
184+
wErr := os.WriteFile(tempFilePath, hclFile.Bytes(), info.Mode())
185+
if wErr != nil {
186+
panic(wErr)
187+
}
188+
os.Rename(tempFilePath, foundFile)
189+
} else {
190+
panic(diag)
191+
}
192+
}
193+
194+
if err != nil {
195+
panic(err)
196+
}
197+
log.Printf("All done.")
198+
}

0 commit comments

Comments
 (0)