Skip to content

Commit

Permalink
chore: generate dependabot configs for examples (#654)
Browse files Browse the repository at this point in the history
* chore: rename function to copy mkdocs config

* chore: extract mkdocs generation to a function

* chore: support generating dependabot updates for example modules

* chore: update dependabot updates for existing examples

* chore: initialize dependabot updates with a default function

* chore: use only one version of go-yaml

* chore: remove quotes from dependabot
  • Loading branch information
mdelapenya committed Dec 5, 2022
1 parent e6dce39 commit 65c6fd8
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 15 deletions.
54 changes: 54 additions & 0 deletions .github/dependabot.yml
Expand Up @@ -12,3 +12,57 @@ updates:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/cockroachdb
schedule:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/datastore
schedule:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/firestore
schedule:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/nginx
schedule:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/pubsub
schedule:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/pulsar
schedule:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/redis
schedule:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/spanner
schedule:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/toxiproxy
schedule:
interval: daily
open-pull-requests-limit: 3
rebase-strategy: disabled
115 changes: 115 additions & 0 deletions examples/dependabot.go
@@ -0,0 +1,115 @@
package main

import (
"io/ioutil"
"path/filepath"

"gopkg.in/yaml.v3"
)

type Updates []Update

type DependabotConfig struct {
Version int `yaml:"version"`
Updates Updates `yaml:"updates"`
}

type Schedule struct {
Interval string `yaml:"interval"`
}

type Update struct {
PackageEcosystem string `yaml:"package-ecosystem"`
Directory string `yaml:"directory"`
Schedule Schedule `yaml:"schedule"`
OpenPullRequestsLimit int `yaml:"open-pull-requests-limit"`
RebaseStrategy string `yaml:"rebase-strategy"`
}

func NewUpdate(example string) Update {
return Update{
Directory: "/examples/" + example,
OpenPullRequestsLimit: 3,
PackageEcosystem: "gomod",
RebaseStrategy: "disabled",
Schedule: Schedule{
Interval: "daily",
},
}
}

// Len is the number of elements in the collection.
func (u Updates) Len() int {
return len(u)
}

// Less reports whether the element with index i
// must sort before the element with index j.
//
// If both Less(i, j) and Less(j, i) are false,
// then the elements at index i and j are considered equal.
// Sort may place equal elements in any order in the final result,
// while Stable preserves the original input order of equal elements.
//
// Less must describe a transitive ordering:
// - if both Less(i, j) and Less(j, k) are true, then Less(i, k) must be true as well.
// - if both Less(i, j) and Less(j, k) are false, then Less(i, k) must be false as well.
//
// Note that floating-point comparison (the < operator on float32 or float64 values)
// is not a transitive ordering when not-a-number (NaN) values are involved.
// See Float64Slice.Less for a correct implementation for floating-point values.
func (u Updates) Less(i, j int) bool {
return u[i].Directory < u[j].Directory
}

// Swap swaps the elements with indexes i and j.
func (u Updates) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}

func getDependabotConfigFile(rootDir string) string {
return filepath.Join(rootDir, ".github", "dependabot.yml")
}

func getDependabotUpdates() ([]Update, error) {
parent, err := getRootDir()
if err != nil {
return nil, err
}

config, err := readDependabotConfig(parent)
if err != nil {
return nil, err
}

return config.Updates, nil
}

func readDependabotConfig(rootDir string) (*DependabotConfig, error) {
configFile := getDependabotConfigFile(rootDir)

file, err := ioutil.ReadFile(configFile)
if err != nil {
return nil, err
}

config := &DependabotConfig{}

err = yaml.Unmarshal(file, config)
if err != nil {
return nil, err
}

return config, nil
}

func writeDependabotConfig(rootDir string, config *DependabotConfig) error {
data, err := yaml.Marshal(config)
if err != nil {
return err
}

file := getDependabotConfigFile(rootDir)

return ioutil.WriteFile(file, data, 0777)
}
81 changes: 81 additions & 0 deletions examples/dependabot_test.go
@@ -0,0 +1,81 @@
package main

import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGetDependabotConfigFile(t *testing.T) {
tmp := t.TempDir()

rootDir := filepath.Join(tmp, "testcontainers-go")
githubDir := filepath.Join(rootDir, ".github")
cfgFile := filepath.Join(githubDir, "dependabot.yml")
err := os.MkdirAll(githubDir, 0777)
require.NoError(t, err)

err = os.WriteFile(cfgFile, []byte{}, 0777)
require.NoError(t, err)

file := getDependabotConfigFile(rootDir)
require.NotNil(t, file)

assert.True(t, strings.HasSuffix(file, filepath.Join("testcontainers-go", ".github", "dependabot.yml")))
}

func TestReadDependabotConfig(t *testing.T) {
tmp := t.TempDir()

rootDir := filepath.Join(tmp, "testcontainers-go")
githubDir := filepath.Join(rootDir, ".github")
err := os.MkdirAll(githubDir, 0777)
require.NoError(t, err)

err = copyInitialDependabotConfig(t, rootDir)
require.NoError(t, err)

config, err := readDependabotConfig(rootDir)
require.NoError(t, err)
require.NotNil(t, config)

assert.Greater(t, len(config.Updates), 0)
}

func TestExamplesHasDependabotEntry(t *testing.T) {
examples, err := getExamples()
require.NoError(t, err)
exampleUpdates, err := getDependabotUpdates()
require.NoError(t, err)

// we have to exclude the main and e2e modules from the examples updates
assert.Equal(t, len(exampleUpdates)-2, len(examples))

// all example modules exist in the dependabot updates
for _, example := range examples {
found := false
for _, exampleUpdate := range exampleUpdates {
dependabotDir := "/examples/" + strings.ToLower(example.Name())

if dependabotDir == exampleUpdate.Directory {
found = true
continue
}
}
assert.True(t, found, "example %s is not present in the dependabot updates", example.Name())
}
}

func copyInitialDependabotConfig(t *testing.T, tmpDir string) error {
projectDir, err := getRootDir()
require.NoError(t, err)

initialConfig, err := readDependabotConfig(projectDir)
require.NoError(t, err)

return writeDependabotConfig(tmpDir, initialConfig)
}
62 changes: 53 additions & 9 deletions examples/main.go
Expand Up @@ -135,6 +135,58 @@ func generate(example Example, rootDir string) error {
}
}

// update examples in mkdocs
err = generateMkdocs(rootDir, exampleLower)
if err != nil {
return err
}

// update examples in dependabot
err = generateDependabotUpdates(rootDir, exampleLower)
if err != nil {
return err
}

fmt.Println("Please go to", example.Lower(), "directory and execute 'go mod tidy' to synchronize the dependencies")
fmt.Println("Commit the modified files and submit a pull request to include them into the project")
fmt.Println("Thanks!")
return nil
}

func generateDependabotUpdates(rootDir string, exampleLower string) error {
// update examples in dependabot
dependabotConfig, err := readDependabotConfig(rootDir)
if err != nil {
return err
}

dependabotExampleUpdates := dependabotConfig.Updates

// make sure the main module is the first element in the list of examples
// and the e2e module is the second element
exampleUpdates := make(Updates, len(dependabotExampleUpdates)-2)
j := 0

for _, exampleUpdate := range dependabotExampleUpdates {
// filter out the index.md file
if exampleUpdate.Directory != "/" && exampleUpdate.Directory != "/e2e" {
exampleUpdates[j] = exampleUpdate
j++
}
}

exampleUpdates = append(exampleUpdates, NewUpdate(exampleLower))
sort.Sort(exampleUpdates)

// prepend the main and e2e modules
exampleUpdates = append([]Update{dependabotExampleUpdates[0], dependabotExampleUpdates[1]}, exampleUpdates...)

dependabotConfig.Updates = exampleUpdates

return writeDependabotConfig(rootDir, dependabotConfig)
}

func generateMkdocs(rootDir string, exampleLower string) error {
// update examples in mkdocs
mkdocsConfig, err := readMkdocsConfig(rootDir)
if err != nil {
Expand Down Expand Up @@ -163,13 +215,5 @@ func generate(example Example, rootDir string) error {

mkdocsConfig.Nav[3].Examples = examplesNav

err = writeMkdocsConfig(rootDir, mkdocsConfig)
if err != nil {
return err
}

fmt.Println("Please go to", example.Lower(), "directory and execute 'go mod tidy' to synchronize the dependencies")
fmt.Println("Commit the modified files and submit a pull request to include them into the project")
fmt.Println("Thanks!")
return nil
return writeMkdocsConfig(rootDir, mkdocsConfig)
}
37 changes: 35 additions & 2 deletions examples/main_test.go
Expand Up @@ -22,7 +22,7 @@ func TestGenerateWrongExampleName(t *testing.T) {
err = os.MkdirAll(githubWorkflowsTmp, 0777)
assert.Nil(t, err)

err = copyInitialConfig(t, rootTmp)
err = copyInitialMkdocsConfig(t, rootTmp)
assert.Nil(t, err)

tests := []struct {
Expand Down Expand Up @@ -65,12 +65,18 @@ func TestGenerate(t *testing.T) {
err = os.MkdirAll(githubWorkflowsTmp, 0777)
assert.Nil(t, err)

err = copyInitialConfig(t, rootTmp)
err = copyInitialMkdocsConfig(t, rootTmp)
assert.Nil(t, err)

originalConfig, err := readMkdocsConfig(rootTmp)
assert.Nil(t, err)

err = copyInitialDependabotConfig(t, rootTmp)
assert.Nil(t, err)

originalDependabotConfig, err := readDependabotConfig(rootTmp)
assert.Nil(t, err)

example := Example{
Name: "foo",
Image: "docker.io/example/foo:latest",
Expand Down Expand Up @@ -113,6 +119,33 @@ func TestGenerate(t *testing.T) {
assertMakefileContent(t, example, filepath.Join(generatedTemplatesDir, "Makefile"))
assertToolsGoContent(t, example, filepath.Join(generatedTemplatesDir, "tools", "tools.go"))
assertMkdocsExamplesNav(t, example, originalConfig, rootTmp)
assertDependabotExamplesUpdates(t, example, originalDependabotConfig, rootTmp)
}

// assert content in the Examples nav from mkdocs.yml
func assertDependabotExamplesUpdates(t *testing.T, example Example, originalConfig *DependabotConfig, rootDir string) {
config, err := readDependabotConfig(rootDir)
assert.Nil(t, err)

examples := config.Updates

assert.Equal(t, len(originalConfig.Updates)+1, len(examples))

// the example should be in the dependabot updates
found := false
for _, ex := range examples {
directory := "/examples/" + example.Lower()
if directory == ex.Directory {
found = true
}
}

assert.True(t, found)

// first item is the main module
assert.Equal(t, "/", examples[0].Directory, examples)
// second item is the e2e module
assert.Equal(t, "/e2e", examples[1].Directory, examples)
}

// assert content example file in the docs
Expand Down

0 comments on commit 65c6fd8

Please sign in to comment.