Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support .env file #11

Merged
merged 1 commit into from
Feb 5, 2025
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
1 change: 1 addition & 0 deletions config/example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ statsServiceName: "stats"
statsContainerName: "stats"
table: "silos"
chainId: "replace-with-actual-chain-id"
pathToEnvFile: "./config/sidecar.env"
8 changes: 8 additions & 0 deletions config/sidecar.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
COIN=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/aurora-is-near/blockscout-frontend/main/public/static/favicon.svg
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/aurora-is-near/blockscout-frontend/main/public/static/aurora_logo_primary_dark_v2.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/aurora-is-near/blockscout-frontend/main/public/static/aurora_logo_primary_light_v2.svg
NEXT_PUBLIC_NETWORK_NAME=0x4e454166
NEXT_PUBLIC_NETWORK_SHORT_NAME=0x4e454166
STATS_CHARTS__TEMPLATE_VALUES__NATIVE_COIN_SYMBOL=ETH
28 changes: 14 additions & 14 deletions internal/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
type Docker struct {
ContainerName string
PathToDockerCompose string
ComposeFile map[string]interface{}
}

func NewDocker() *Docker {
Expand Down Expand Up @@ -75,23 +76,22 @@ func (d *Docker) RecreateContainers(containers []Container) error {
}

// ReadComposeFile reads and parses the Docker compose file
func (d *Docker) ReadComposeFile() (map[string]interface{}, error) {
func (d *Docker) ReadComposeFile() error {
data, err := os.ReadFile(d.PathToDockerCompose)
if err != nil {
return nil, fmt.Errorf("failed to read compose file: %w", err)
return fmt.Errorf("failed to read compose file: %w", err)
}

var config map[string]interface{}
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("failed to parse compose file: %w", err)
if err := yaml.Unmarshal(data, &d.ComposeFile); err != nil {
return fmt.Errorf("failed to parse compose file: %w", err)
}

return config, nil
return nil
}

// WriteComposeFile writes the updated compose configuration back to the file
func (d *Docker) WriteComposeFile(compose map[string]interface{}) error {
data, err := yaml.Marshal(compose)
func (d *Docker) WriteComposeFile() error {
data, err := yaml.Marshal(d.ComposeFile)
if err != nil {
return fmt.Errorf("failed to marshal compose file: %w", err)
}
Expand All @@ -105,21 +105,21 @@ func (d *Docker) WriteComposeFile(compose map[string]interface{}) error {

// UpdateServiceEnv updates environment variables for a specific service in the compose file
// Returns the updated compose configuration and whether any changes were made
func (d *Docker) UpdateServiceEnv(compose map[string]interface{}, serviceName string, env map[string]interface{}) (map[string]interface{}, bool, error) {
func (d *Docker) UpdateServiceEnv(serviceName string, env map[string]string) (bool, error) {
updated := false
services, ok := compose["services"].(map[string]interface{})
services, ok := d.ComposeFile["services"].(map[string]interface{})
if !ok {
return nil, updated, fmt.Errorf("services section not found")
return false, fmt.Errorf("services section not found")
}

service, ok := services[serviceName].(map[string]interface{})
if !ok {
return nil, updated, fmt.Errorf("service %s not found", serviceName)
return false, fmt.Errorf("service %s not found", serviceName)
}

serviceEnv, ok := service["environment"].(map[string]interface{})
if !ok {
return nil, updated, fmt.Errorf("environment section not found in service")
return false, fmt.Errorf("environment section not found in service")
}

for key, value := range env {
Expand All @@ -129,7 +129,7 @@ func (d *Docker) UpdateServiceEnv(compose map[string]interface{}, serviceName st
}
}

return compose, updated, nil
return updated, nil
}

// UniqueContainerNames returns a sorted list of unique container names
Expand Down
121 changes: 121 additions & 0 deletions internal/env/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package env

import (
"bufio"
"fmt"
"os"
"sort"
"strings"

"github.com/spf13/viper"
)

type Env struct {
PathToEnvFile string
EnvFile map[string]string
}

func NewEnv() *Env {
return &Env{
PathToEnvFile: viper.GetString("pathToEnvFile"),
EnvFile: make(map[string]string),
}
}

// ReadEnvFile reads and parses the environment file
func (e *Env) ReadEnvFile() error {
file, err := os.Open(e.PathToEnvFile)
if err != nil {
return fmt.Errorf("failed to read env file: %w", err)
}
defer file.Close()

scanner := bufio.NewScanner(file)

for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}

parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
continue
}

key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
// Remove quotes if present
value = strings.Trim(value, `"'`)

e.EnvFile[key] = value
}

if err := scanner.Err(); err != nil {
return fmt.Errorf("error scanning env file: %w", err)
}

return nil
}

// WriteEnvFile writes the environment variables back to the file
func (e *Env) WriteEnvFile() error {
file, err := os.Create(e.PathToEnvFile)
if err != nil {
return fmt.Errorf("failed to create env file: %w", err)
}
defer file.Close()

writer := bufio.NewWriter(file)

// Sort keys for consistent output
keys := make([]string, 0, len(e.EnvFile))
for k := range e.EnvFile {
keys = append(keys, k)
}
sort.Strings(keys)

for _, key := range keys {
value := e.EnvFile[key]
// Add quotes if value contains spaces
if strings.Contains(value, " ") {
value = fmt.Sprintf(`"%s"`, value)
}

line := fmt.Sprintf("%s=%s\n", key, value)
if _, err := writer.WriteString(line); err != nil {
return fmt.Errorf("failed to write line to env file: %w", err)
}
}

if err := writer.Flush(); err != nil {
return fmt.Errorf("failed to flush env file: %w", err)
}

return nil
}

// UpdateEnvVars updates environment variables in the env file
// Returns whether any changes were made
func (e *Env) UpdateEnvVars(updates map[string]string) (bool, error) {
err := e.ReadEnvFile()
if err != nil {
return false, fmt.Errorf("failed to read env file: %w", err)
}

updated := false
for key, newValue := range updates {
if currentValue, exists := e.EnvFile[key]; !exists || currentValue != newValue {
e.EnvFile[key] = newValue
updated = true
}
}

if updated {
if err := e.WriteEnvFile(); err != nil {
return false, fmt.Errorf("failed to write env file: %w", err)
}
}

return updated, nil
}
16 changes: 1 addition & 15 deletions internal/handlers/coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ func (h *CoinHandler) Handle(record *Record) HandlerResult {
return result
}

compose, err := h.docker.ReadComposeFile()
if err != nil {
result.Error = fmt.Errorf("failed to read compose file: %w", err)
return result
}

updates := []EnvUpdate{
{
ServiceName: viper.GetString("frontendServiceName"),
Expand All @@ -56,12 +50,9 @@ func (h *CoinHandler) Handle(record *Record) HandlerResult {
},
}

// Define environment updates for each service

// Apply updates to each service
for _, env := range updates {
var updated bool
compose, updated, err = h.docker.UpdateServiceEnv(compose, env.ServiceName, map[string]interface{}{
updated, err := h.UpdateServiceEnv(env.ServiceName, map[string]string{
env.Key: env.Value,
})
if err != nil {
Expand All @@ -77,11 +68,6 @@ func (h *CoinHandler) Handle(record *Record) HandlerResult {
}
}

if err = h.docker.WriteComposeFile(compose); err != nil {
result.Error = fmt.Errorf("failed to write compose file: %w", err)
return result
}

return result
}

Expand Down
20 changes: 4 additions & 16 deletions internal/handlers/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,12 @@ func (h *ImageHandler) Handle(record *Record) HandlerResult {
return result
}

compose, err := h.docker.ReadComposeFile()
if err != nil {
result.Error = fmt.Errorf("failed to read compose file: %w", err)
return result
}

frontendServiceName := viper.GetString("frontendServiceName")
frontendContainerName := viper.GetString("frontendContainerName")

updates := map[string]map[string]interface{}{
frontendServiceName: {},
// Initialize updates with string map
updates := map[string]map[string]string{
frontendServiceName: make(map[string]string),
}

// Validate and update light logo URL
Expand All @@ -74,8 +69,7 @@ func (h *ImageHandler) Handle(record *Record) HandlerResult {

// Apply updates to services
for service, env := range updates {
var updated bool
compose, updated, err = h.docker.UpdateServiceEnv(compose, service, env)
updated, err := h.UpdateServiceEnv(service, env)
if err != nil {
result.Error = fmt.Errorf("failed to update %s service environment: %w", service, err)
return result
Expand All @@ -91,12 +85,6 @@ func (h *ImageHandler) Handle(record *Record) HandlerResult {
}
}

err = h.docker.WriteComposeFile(compose)
if err != nil {
result.Error = fmt.Errorf("failed to write compose file: %w", err)
return result
}

return result
}

Expand Down
25 changes: 7 additions & 18 deletions internal/handlers/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,20 @@ func (h *NameHandler) Handle(record *Record) HandlerResult {
return result
}

compose, err := h.docker.ReadComposeFile()
if err != nil {
result.Error = fmt.Errorf("failed to read compose file: %w", err)
return result
}

frontendServiceName := viper.GetString("frontendServiceName")
frontendContainerName := viper.GetString("frontendContainerName")

updates := map[string]map[string]interface{}{
frontendServiceName: {},
// Create updates with string values
updates := map[string]map[string]string{
frontendServiceName: {
"NEXT_PUBLIC_NETWORK_NAME": record.Name,
"NEXT_PUBLIC_NETWORK_SHORT_NAME": record.Name,
},
}
updates[frontendServiceName]["NEXT_PUBLIC_NETWORK_NAME"] = record.Name
updates[frontendServiceName]["NEXT_PUBLIC_NETWORK_SHORT_NAME"] = record.Name

// Apply updates to services
for service, env := range updates {
var updated bool
compose, updated, err = h.docker.UpdateServiceEnv(compose, service, env)
updated, err := h.UpdateServiceEnv(service, env)
if err != nil {
result.Error = fmt.Errorf("failed to update %s service environment: %w", service, err)
return result
Expand All @@ -63,12 +58,6 @@ func (h *NameHandler) Handle(record *Record) HandlerResult {
}
}

err = h.docker.WriteComposeFile(compose)
if err != nil {
result.Error = fmt.Errorf("failed to write compose file: %w", err)
return result
}

return result
}

Expand Down
Loading
Loading