mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-12 12:17:47 +08:00
feat: add support for stripping inline comments in .env files and make Docker host port configurable via DS2API_HOST_PORT
This commit is contained in:
@@ -47,7 +47,7 @@ func loadDotEnvFromPath(path string) error {
|
||||
if _, exists := os.LookupEnv(key); exists {
|
||||
continue
|
||||
}
|
||||
if err := os.Setenv(key, normalizeDotEnvValue(strings.TrimSpace(value))); err != nil {
|
||||
if err := os.Setenv(key, normalizeDotEnvValue(trimDotEnvValue(strings.TrimSpace(value)))); err != nil {
|
||||
return fmt.Errorf("%s:%d set env %q: %w", path, i+1, key, err)
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,62 @@ func loadDotEnvFromPath(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Preserve quoted values, but drop Compose-style inline comments from unquoted values.
|
||||
func trimDotEnvValue(raw string) string {
|
||||
if raw == "" {
|
||||
return raw
|
||||
}
|
||||
|
||||
switch raw[0] {
|
||||
case '"':
|
||||
if trimmed, ok := trimQuotedDotEnvValue(raw, '"'); ok {
|
||||
return trimmed
|
||||
}
|
||||
case '\'':
|
||||
if trimmed, ok := trimQuotedDotEnvValue(raw, '\''); ok {
|
||||
return trimmed
|
||||
}
|
||||
default:
|
||||
if idx := inlineDotEnvCommentStart(raw); idx >= 0 {
|
||||
return strings.TrimSpace(raw[:idx])
|
||||
}
|
||||
}
|
||||
|
||||
return raw
|
||||
}
|
||||
|
||||
func trimQuotedDotEnvValue(raw string, quote byte) (string, bool) {
|
||||
escaped := false
|
||||
for i := 1; i < len(raw); i++ {
|
||||
ch := raw[i]
|
||||
if quote == '"' && escaped {
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
if quote == '"' && ch == '\\' {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
if ch == quote {
|
||||
return strings.TrimSpace(raw[:i+1]), true
|
||||
}
|
||||
}
|
||||
return raw, false
|
||||
}
|
||||
|
||||
func inlineDotEnvCommentStart(raw string) int {
|
||||
for i := 1; i < len(raw); i++ {
|
||||
if raw[i] == '#' && isDotEnvCommentSpacer(raw[i-1]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func isDotEnvCommentSpacer(b byte) bool {
|
||||
return b == ' ' || b == '\t'
|
||||
}
|
||||
|
||||
func normalizeDotEnvValue(raw string) string {
|
||||
if len(raw) < 2 {
|
||||
return raw
|
||||
|
||||
@@ -3,6 +3,7 @@ package config
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -67,6 +68,57 @@ func TestLoadDotEnvIgnoresMissingFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadDotEnvStripsInlineCommentsFromUnquotedValues(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
oldWD, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("getwd: %v", err)
|
||||
}
|
||||
if err := os.Chdir(dir); err != nil {
|
||||
t.Fatalf("chdir temp dir: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = os.Chdir(oldWD)
|
||||
})
|
||||
|
||||
const plainKey = "DS2API_TEST_DOTENV_PLAIN"
|
||||
const hashKey = "DS2API_TEST_DOTENV_HASH"
|
||||
const quotedKey = "DS2API_TEST_DOTENV_QUOTED_COMMENT"
|
||||
const exportKey = "DS2API_TEST_DOTENV_EXPORT"
|
||||
|
||||
unsetEnv(t, plainKey)
|
||||
unsetEnv(t, hashKey)
|
||||
unsetEnv(t, quotedKey)
|
||||
unsetEnv(t, exportKey)
|
||||
|
||||
content := strings.Join([]string{
|
||||
plainKey + "=5001 # local",
|
||||
hashKey + "=5001#local",
|
||||
quotedKey + `="5001 # local" # keep the inner hash`,
|
||||
"export " + exportKey + "=enabled # exported",
|
||||
}, "\n") + "\n"
|
||||
if err := os.WriteFile(filepath.Join(dir, ".env"), []byte(content), 0o644); err != nil {
|
||||
t.Fatalf("write .env: %v", err)
|
||||
}
|
||||
|
||||
if err := LoadDotEnv(); err != nil {
|
||||
t.Fatalf("LoadDotEnv() error: %v", err)
|
||||
}
|
||||
|
||||
if got := os.Getenv(plainKey); got != "5001" {
|
||||
t.Fatalf("expected inline comment to be stripped, got %q", got)
|
||||
}
|
||||
if got := os.Getenv(hashKey); got != "5001#local" {
|
||||
t.Fatalf("expected hash without preceding whitespace to remain, got %q", got)
|
||||
}
|
||||
if got := os.Getenv(quotedKey); got != "5001 # local" {
|
||||
t.Fatalf("expected quoted value to preserve hash text, got %q", got)
|
||||
}
|
||||
if got := os.Getenv(exportKey); got != "enabled" {
|
||||
t.Fatalf("expected export syntax to load, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func unsetEnv(t *testing.T, key string) {
|
||||
t.Helper()
|
||||
old, had := os.LookupEnv(key)
|
||||
|
||||
Reference in New Issue
Block a user