Skip to content

Commit ccaaaff

Browse files
fix(config): stop parsing the syncerd binary as configuration, and remove the dead workflow (#50)
* fix(config): stop parsing the syncerd binary as configuration viper matches an extensionless file when a config type is set, and the extensionless file sitting in a working directory is very often the syncerd binary: make build writes ./syncerd, so running ./syncerd sync beside it made the tool read its own binary as YAML. The error that produced, "yaml: control characters are not allowed", names neither the file nor the cause. This repository's own scheduled workflow has failed that way on every run since at least June. Discovery now resolves the file itself and accepts only syncerd.yaml or syncerd.yml, in the working directory or ./config. When there is none it says so and lists everywhere it looked, which is the answer the operator actually needs. * chore(ci): remove the scheduled SyncerD workflow It ran syncerd sync --once against a syncerd.yaml this repository has never contained, so it failed on every run since at least June, and its push trigger filtered on that same non existent path. The config discovery fix alongside this turns its failure from a baffling parse error into a clear one, which is an improvement to the message and not to the workflow. Removed rather than repaired. Making it work would mean deciding what this organisation wants mirrored and committing that as configuration, which is a product decision rather than a repair, and a workflow that has only ever failed teaches everyone to ignore a red mark on master. --------- Co-authored-by: Anmol Nagpal <ianmolnagpal@gmail.com>
1 parent f8b5667 commit ccaaaff

4 files changed

Lines changed: 102 additions & 99 deletions

File tree

.github/workflows/syncerd.yml

Lines changed: 0 additions & 96 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
- A failed state save on the fail-fast path was silent, so everything copied before the abort was copied again on the next run with no explanation
2222
- `git-sync` could not authenticate to Azure DevOps over git in `pat` mode, fixed in v0.2.1 and described there
2323

24+
### Removed
25+
- The `SyncerD` scheduled workflow, which ran `syncerd sync --once` against a `syncerd.yaml` this repository has never contained. It had failed on every run since at least June, and its push trigger filtered on a path that does not exist. Deleted rather than repaired: the dogfooding it was meant to do is not wanted, and a workflow that has only ever failed is worse than none
26+
2427
### Fixed
28+
- SyncerD parsed its own binary as its configuration. Config discovery accepted an extensionless file named `syncerd`, which is exactly what `make build` writes into the working directory, so running `./syncerd sync` beside the binary failed with `yaml: control characters are not allowed`, an error that points at nothing. Discovery now accepts only a real `syncerd.yaml` or `syncerd.yml`, and says where it looked when it finds neither
2529
- A mirrored pull request with no labels failed to update, taking the whole pull request with it. Once the mirror owned labels, an empty set was sent as null rather than as an empty array, and GitHub rejects that with 422. Most pull requests carry no labels, so this affected most of them. Found by the live suite on its first real run
2630

2731
### Added

internal/config/config.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package config
22

33
import (
44
"fmt"
5+
"os"
6+
"path/filepath"
57
"strings"
68

79
"github.com/spf13/viper"
@@ -54,16 +56,56 @@ type SlackConfig struct {
5456
MessageFormat string `mapstructure:"message_format"`
5557
}
5658

59+
// configSearchPaths are the directories searched for a config file, and
60+
// configSearchNames the file names accepted in each, in order.
61+
var (
62+
configSearchPaths = []string{".", "./config"}
63+
configSearchNames = []string{"syncerd.yaml", "syncerd.yml"}
64+
)
65+
66+
// findConfigFile returns the first config file present, or an error naming
67+
// everywhere it looked.
68+
//
69+
// Only a real extension counts. An extensionless "syncerd" is the compiled
70+
// binary, not configuration.
71+
func findConfigFile() (string, error) {
72+
var tried []string
73+
for _, dir := range configSearchPaths {
74+
for _, name := range configSearchNames {
75+
candidate := filepath.Join(dir, name)
76+
tried = append(tried, candidate)
77+
info, err := os.Stat(candidate)
78+
if err != nil || info.IsDir() {
79+
continue
80+
}
81+
return candidate, nil
82+
}
83+
}
84+
return "", fmt.Errorf("no config file found; looked for %s. Pass --config to name one explicitly",
85+
strings.Join(tried, ", "))
86+
}
87+
5788
func Load(configPath string) (*Config, error) {
5889
viper.SetConfigType("yaml")
5990
viper.SetConfigName("syncerd")
6091

6192
if configPath != "" {
6293
viper.SetConfigFile(configPath)
6394
} else {
64-
// Look for config in current directory
65-
viper.AddConfigPath(".")
66-
viper.AddConfigPath("./config")
95+
// Resolve the file here rather than letting viper search.
96+
//
97+
// viper matches an extensionless file when a config type is set,
98+
// and the file it finds in a working directory is very often the
99+
// syncerd binary itself: `make build` writes ./syncerd, and running
100+
// ./syncerd sync in that directory made the tool parse its own
101+
// binary as YAML. The error that produced, "control characters are
102+
// not allowed", says nothing about what actually happened, and the
103+
// project's own scheduled workflow failed that way for months.
104+
found, err := findConfigFile()
105+
if err != nil {
106+
return nil, err
107+
}
108+
viper.SetConfigFile(found)
67109
}
68110

69111
// Environment variables: SYNCERD_ prefix, with nested keys mapped via

internal/config/config_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,56 @@ func TestValidateImageSyncAcceptsValidConfig(t *testing.T) {
7878
t.Fatalf("expected valid config, got %v", err)
7979
}
8080
}
81+
82+
func TestABinaryNamedSyncerdIsNotTreatedAsConfig(t *testing.T) {
83+
// `make build` writes ./syncerd, and viper matches an extensionless
84+
// file when a config type is set, so running ./syncerd sync in that
85+
// directory made the tool parse its own binary as YAML. The error was
86+
// "control characters are not allowed", which points at nothing, and
87+
// the project's own scheduled workflow failed that way for months.
88+
dir := t.TempDir()
89+
if err := os.WriteFile(filepath.Join(dir, "syncerd"), []byte("\x7fELF\x02\x01\x01\x00binary"), 0o755); err != nil {
90+
t.Fatalf("write: %v", err)
91+
}
92+
93+
cwd, err := os.Getwd()
94+
if err != nil {
95+
t.Fatalf("getwd: %v", err)
96+
}
97+
if err := os.Chdir(dir); err != nil {
98+
t.Fatalf("chdir: %v", err)
99+
}
100+
t.Cleanup(func() { _ = os.Chdir(cwd) })
101+
102+
_, err = findConfigFile()
103+
if err == nil {
104+
t.Fatal("an extensionless binary must not be accepted as a config file")
105+
}
106+
if !strings.Contains(err.Error(), "no config file found") {
107+
t.Errorf("the error should say what is missing, got %v", err)
108+
}
109+
}
110+
111+
func TestFindConfigFilePrefersAnExplicitYAML(t *testing.T) {
112+
dir := t.TempDir()
113+
if err := os.WriteFile(filepath.Join(dir, "syncerd"), []byte("binary"), 0o755); err != nil {
114+
t.Fatalf("write: %v", err)
115+
}
116+
if err := os.WriteFile(filepath.Join(dir, "syncerd.yaml"), []byte("source:\n type: dockerhub\n"), 0o644); err != nil {
117+
t.Fatalf("write: %v", err)
118+
}
119+
120+
cwd, _ := os.Getwd()
121+
if err := os.Chdir(dir); err != nil {
122+
t.Fatalf("chdir: %v", err)
123+
}
124+
t.Cleanup(func() { _ = os.Chdir(cwd) })
125+
126+
got, err := findConfigFile()
127+
if err != nil {
128+
t.Fatalf("find: %v", err)
129+
}
130+
if filepath.Base(got) != "syncerd.yaml" {
131+
t.Errorf("found %q, want syncerd.yaml", got)
132+
}
133+
}

0 commit comments

Comments
 (0)