Skip to content
Merged
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
3 changes: 1 addition & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ release_branches: &release_branches # Release branch

## <</Stencil::Block>>

jobs:
{}
jobs: {}
## <<Stencil::Block(circleJobs)>>

## <</Stencil::Block>>
Expand Down
2 changes: 1 addition & 1 deletion .vscode/private.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
MY_NAMESPACE="devbase--bento1a"

// <<Stencil::Block(vscodeEnvVars)>>

// <</Stencil::Block>>
97 changes: 97 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# devbase

Ignore lines containing `Stencil::Block`; they are areas in your generated code that you'd like to persist across runs and are repository specific. These lines are for the template generator and do not contain agent instructions.

## Description

<!-- <<Stencil::Block(agentsProjectOverview)>> -->
A collection of scripts and ci configuration
<!-- <</Stencil::Block>> -->

## Project overview

<!-- <<Stencil::Block(projectOverview)>> -->

<!-- <</Stencil::Block>> -->

## Generic commands

```bash
# stencil
stencil # Run stencil program with arguments specified in service.yaml file

# mise
mise --help # Show help for mise commands.

# make
make fmt # Run formatters on project's code.
make lint # Run linters on project's code.

# golang
make gogenerate # Run go generate to create any generated code, such as protobufs or Kubernetes CRDs.
go mod tidy # Ensure your go.mod and go.sum files are up to date.
# <<Stencil::Block(customCommands)>>

# <</Stencil::Block>>
```

## Directory structure

* service.yaml: File used as configuration for `stencil` program containing additional arguments and stencil modules to use
* stencil.lock: A lockfile for Stencil which also declares which files in the repo are managed, and which module manages it. Third party generated files are not cataloged.
* CONTRIBUTING.md: File containing guidelines for contributing to the project.
* docs/: Directory used to store documentation files and reference materials for the project.
* `scripts/`: internal development shell scripts _(**deprecated**, prefer to use `mise` tasks when appropriate)_
* `.vscode/`: VSCode configuration files
<!-- <<Stencil::Block(directoryStructureCustom)>> -->

<!-- <</Stencil::Block>> -->

If you need more context, you can find more information in `docs/` directory.

## References table

| Description | Reference |
|----|----|
| Stencil commands | [docs/agents/stencil-commands.md](./docs/agents/stencil-commands.md) |
| Idiomatic Go practices | [webpage](https://dmitri.shuralyov.com/idiomatic-go) |
| Effective Go | [webpage](https://go.dev/doc/effective_go) |
<!-- <<Stencil::Block(referencesTableCustom)>> -->

<!-- <</Stencil::Block>> -->

## Boundaries

### Always
- Run `go mod tidy` after adding, removing or upgrading Go dependencies
- Run `make gogenerate` after modifying protobuf definitions or interfaces with generated code
- Add context to errors using `fmt.Errorf("...: %w", err)`
- Prefer `gotest.tools/v3/assert` in tests over `github.com/stretchr/testify` or hand-rolled assertions
<!-- <<Stencil::Block(agentsBoundariesAlwaysCustom)>> -->

<!-- <</Stencil::Block>> -->

### Ask

Before each scenario in the following list, ask the user if they allow the change to occur. For every question, include: root reason for change, list the tradeoffs for the change.

- Changing public API signatures (exported functions, types, or interfaces)
- Adding new external dependencies
- Bumping major versions of dependencies
- Changing database schema or migration files
<!-- <<Stencil::Block(agentsBoundariesAskCustom)>> -->

<!-- <</Stencil::Block>> -->

### Never

- Commit secrets, credentials, API keys, or tokens
- Use `panic()` in production code paths
<!-- <<Stencil::Block(agentsBoundariesNeverCustom)>> -->

<!-- <</Stencil::Block>> -->

## Other
<!-- <<Stencil::Block(agentsOtherCustom)>> -->

<!-- <</Stencil::Block>> -->
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
2 changes: 1 addition & 1 deletion cortex.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ info:
repo: https://github.com/getoutreach/devbase
framework: stencil
language: Golang
stencil_version: v1.43.2
stencil_version: v1.45.1
golang_version: 1.25.7
cli: false
service: false
Expand Down
2 changes: 1 addition & 1 deletion devbase.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
// Description: This file is the entrypoint for the devbase library.

// Package devbase does nothing. This is an empty file
package devbase //nolint:revive // Why: This nolint is here just in case your project name contains any of [-_].
package devbase
27 changes: 27 additions & 0 deletions docs/agents/stencil-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Stencil Commands

Stencil commands are exposed as `mise` tasks. To list all available stencil tasks:

```bash
mise tasks | grep stencil
```

## Common tasks

| Task | Description |
|---|---|
| `stencil` | Run Stencil without updating module versions |
| `stencil:upgrade` | Run Stencil and update module versions if available |

## Usage

```bash
mise run stencil
mise run stencil:upgrade
```

For full task details:

```bash
mise tasks info <task-name>
```
10 changes: 5 additions & 5 deletions e2e/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type Devenv struct {
} `yaml:"dependencies"`
}

// FromFile parses the devenv.yaml file and returns a DevenvConfig
// FromFile parses the devenv.yaml file and returns a DevenvConfig.
func FromFile(confPath string) (*Devenv, error) {
f, err := os.Open(confPath)
if err != nil {
Expand All @@ -48,7 +48,7 @@ func FromFile(confPath string) (*Devenv, error) {
return &dc, nil
}

// ReadServiceName reads service name from service.yaml
// ReadServiceName reads service name from service.yaml.
func ReadServiceName() (string, error) {
configFileName := "service.yaml"
b, err := os.ReadFile(configFileName)
Expand All @@ -69,7 +69,7 @@ func ReadServiceName() (string, error) {
return conf.Name, nil
}

// FromGitHub reads and parses DevenvConfig from GitHub
// FromGitHub reads and parses DevenvConfig from GitHub.
func FromGitHub(ctx context.Context, conf *box.Config, serviceName string,
gh *github.Client, configFileName string,
) (*Devenv, error) {
Expand All @@ -88,9 +88,9 @@ func FromGitHub(ctx context.Context, conf *box.Config, serviceName string,
return &dc, nil
}

// getDependencies returns all dependencies
// GetAllDependencies returns all dependencies.
func (c *Devenv) GetAllDependencies() []string {
deps := make([]string, 0)
deps := make([]string, 0, len(c.Dependencies.Required)+len(c.Dependencies.Optional))
deps = append(deps, c.Dependencies.Required...)
deps = append(deps, c.Dependencies.Optional...)
return deps
Expand Down
55 changes: 24 additions & 31 deletions e2e/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ package main
import (
"context"
"encoding/xml"
"fmt"
"go/build"
"os"
"os/exec"
"path/filepath"
"slices"
"strings"
"sync"

Expand All @@ -26,23 +26,23 @@ import (
"github.com/rs/zerolog/log"
)

// flagship is the name of the flagship
// flagship is the name of the flagship.
const flagship = "flagship"

// junitTestResultPath path to test results after we run (devenv apps e2e)
// junitTestResultPath path to test results after we run (devenv apps e2e).
const junitTestResultPath = "./bin/unit-tests.xml"

// devenvAlreadyExists contains message when devenv exists
// devenvAlreadyExists contains message when devenv exists.
const devenvAlreadyExists = "Re-using existing cluster, this may lead to a non-reproducible failure/success. " +
"To ensure a clean operation, run `devenv destroy` before running tests"

// osStdInOutErr is a helper function to use the os stdin/out/err
// osStdInOutErr is a helper function to use the os stdin/out/err.
func osStdInOutErr(c *exec.Cmd) *exec.Cmd {
c.Stdin = os.Stdin
return osStdOutErr(c)
}

// osStdOutErr is a helper function to use the os stdout/err
// osStdOutErr is a helper function to use the os stdout/err.
func osStdOutErr(c *exec.Cmd) *exec.Cmd {
c.Stdout = os.Stdout
c.Stderr = os.Stderr
Expand Down Expand Up @@ -71,7 +71,7 @@ func BuildDependenciesList(ctx context.Context, conf *box.Config) ([]string, err
}

// findDependenciesInRepo finds the dependencies in a repository
// at all of the possible paths
// at all of the possible paths.
func findDependenciesInRepo(ctx context.Context, conf *box.Config, serviceName string) (set.Set[string], error) {
possibleFiles := []string{"devenv.yaml", "noncompat-service.yaml", "service.yaml"}
gh, err := githubauth.NewClient()
Expand All @@ -93,7 +93,7 @@ func findDependenciesInRepo(ctx context.Context, conf *box.Config, serviceName s
if dc == nil {
log.Warn().Str("service", serviceName).
Msgf("Failed to find any of the following %v, will not try to calculate dependencies of this service", possibleFiles)
return nil, nil
return make(set.Set[string]), nil
}

// We deploy just required transitive dependencies
Expand All @@ -105,7 +105,7 @@ func findDependenciesInRepo(ctx context.Context, conf *box.Config, serviceName s
// grabDependencies traverses the dependency tree by calculating
// it on the fly via git cloning of the dependencies. Passed in
// is a hash map used to prevent infinite recursion and de-duplicate
// dependencies. New dependencies are inserted into the provided hash-map
// dependencies. New dependencies are inserted into the provided hash-map.
func grabDependencies(ctx context.Context, conf *box.Config, deps set.Set[string], serviceName string) error {
// We special case this here to ensure we don't fail on deps that haven't updated
// their dependency yet.
Expand Down Expand Up @@ -139,8 +139,8 @@ func grabDependencies(ctx context.Context, conf *box.Config, deps set.Set[string
return nil
}

// provisionNew destroys and re-provisions a devenv
func provisionNew(ctx context.Context, target string) error { // nolint:unparam // Why: keeping in the interface for now
// provisionNew destroys and re-provisions a devenv.
func provisionNew(ctx context.Context, target string) error {
devenv, err := newDevenvCmd(ctx, "destroy")
if err != nil {
return errors.Wrap(err, "Failed to create devenv destroy command")
Expand All @@ -155,17 +155,17 @@ func provisionNew(ctx context.Context, target string) error { // nolint:unparam
return nil
}

// runDevconfig executes devconfig command
// runDevconfig executes devconfig command.
func runDevconfig(ctx context.Context) error {
out, err := exec.CommandContext(ctx, "./scripts/shell-wrapper.sh", "devconfig.sh").CombinedOutput()
if err != nil {
return fmt.Errorf("%s", out)
return errors.New(string(out))
}
return nil
}

// shouldRunE2ETests denotes whether or not this needs to actually
// run
// run.
func shouldRunE2ETests() (bool, error) {
var runEndToEndTests bool

Expand Down Expand Up @@ -235,16 +235,14 @@ func runE2ETestsUsingDevspace(ctx context.Context, conf *box.Config) error {
}

var wg sync.WaitGroup
wg.Add(1)

go func() {
defer wg.Done()
wg.Go(func() {
log.Info().Msg("Building binaries for devspace pod")
if err := osStdInOutErr(exec.CommandContext(ctx, "make", "devspace")).Run(); err != nil {
log.Error().Err(err).Msg("Error when building for devspace")
panic(err)
}
}()
})

log.Info().Msgf("Deploying latest stable version of %s application into cluster together with dependencies", serviceName)
if err := runDevenvPassthrough(ctx, "apps", "deploy", "--with-deps", serviceName); err != nil {
Expand Down Expand Up @@ -274,7 +272,7 @@ func runE2ETestsUsingDevspace(ctx context.Context, conf *box.Config) error {
return nil
}

// parseResultFromJunitReport parses if tests succeeded from junit xml file
// parseResultFromJunitReport parses if tests succeeded from junit xml file.
func parseResultFromJunitReport() (bool, error) {
type Testsuite struct {
XMLName xml.Name `xml:"testsuites"`
Expand Down Expand Up @@ -364,7 +362,6 @@ func main() { //nolint:funlen,gocyclo // Why: there are no reusable parts to ext

err := provisionDevenv(ctx, conf)
if err != nil {
//nolint:gocritic // Why: need to get exit code >0
log.Fatal().Err(err).Msg("Failed to provision devenv")
return
}
Expand All @@ -376,7 +373,6 @@ func main() { //nolint:funlen,gocyclo // Why: there are no reusable parts to ext
wg.Wait() // To ensure that docker build is finished
} else {
log.Info().
//nolint:lll // Why: Message to user
Msg(devenvAlreadyExists)
}
}
Expand Down Expand Up @@ -445,7 +441,7 @@ func main() { //nolint:funlen,gocyclo // Why: there are no reusable parts to ext
if err != nil {
log.Fatal().Err(err).Msg("Failed to run localizer")
}
defer closer()
defer closer(ctx)
}

log.Info().Msg("Running e2e tests")
Expand All @@ -455,7 +451,7 @@ func main() { //nolint:funlen,gocyclo // Why: there are no reusable parts to ext
}
}

// provisionDevenv provisions devenv in correct target based on application dependencies
// provisionDevenv provisions devenv in correct target based on application dependencies.
func provisionDevenv(ctx context.Context, conf *box.Config) error {
deps, err := BuildDependenciesList(ctx, conf)
if err != nil {
Expand All @@ -466,13 +462,8 @@ func provisionDevenv(ctx context.Context, conf *box.Config) error {
target := "base"
if os.Getenv("PROVISION_TARGET") != "" {
target = os.Getenv("PROVISION_TARGET")
} else {
for _, d := range deps {
if d == "outreach" {
target = flagship
break
}
}
} else if slices.Contains(deps, "outreach") {
target = flagship
}

log.Info().Strs("deps", deps).Str("target", target).Msg("Provisioning devenv")
Expand All @@ -492,10 +483,12 @@ func isDevenvProvisioned(ctx context.Context) bool {
}

func runningInCI() bool {
return os.Getenv("CI") == "true" //nolint:goconst // Why: true == true
return os.Getenv("CI") == "true"
}

// devenvPath is the cached path to the devenv binary.
//
//nolint:gochecknoglobals // Why: cache variable
var devenvPath = ""

// findDevenv finds the path to the devenv binary, via mise or PATH.
Expand Down
Loading