diff --git a/main.go b/main.go index 57447d1..37ef7da 100644 --- a/main.go +++ b/main.go @@ -6,9 +6,12 @@ import ( "encoding/json" "flag" "fmt" + "io" "io/fs" "io/ioutil" "log" + "net/http" + "net/url" "os" "os/signal" "path" @@ -21,13 +24,15 @@ import ( ) var ( - Debug = os.Getenv("DEBUG") == "true" - ConstTitle = os.Getenv("YAML_C_TITLE") - ConstSeason = os.Getenv("YAML_C_SEASON") - ConstEpisode = os.Getenv("YAML_C_EPISODE") - ConstOutd = os.Getenv("YAML_O") - Dry = os.Getenv("YAML_D") == "true" || os.Getenv("DRY") == "true" - ConstPatterns = os.Getenv("YAML_P") + Debug = os.Getenv("DEBUG") == "true" + ConstTitle = os.Getenv("YAML_C_TITLE") + ConstSeason = os.Getenv("YAML_C_SEASON") + ConstEpisode = os.Getenv("YAML_C_EPISODE") + ConstOutd = os.Getenv("YAML_O") + Dry = os.Getenv("YAML_D") == "true" || os.Getenv("DRY") == "true" + ConstPatterns = os.Getenv("YAML_P") + WebhookOnRecursiveMiss = os.Getenv("RECURSIVE_MISSING_WEBHOOK") + WebhookOnRecursiveMissCacheD = os.Getenv("RECURSIVE_MISSING_WEBHOOK_CACHE_D") ) type Yaml struct { @@ -81,6 +86,32 @@ func Recursive(ctx context.Context) error { p := path.Join(d, YamlFile) if _, err := os.Stat(p); err != nil { + log.Printf("%s has no %s", d, YamlFile) + if WebhookOnRecursiveMiss != "" && WebhookOnRecursiveMissCacheD != "" { + cacheP := regexp.MustCompile(`[^a-zA-Z0-9]`).ReplaceAllString(p, `_`) + cacheP = path.Join(WebhookOnRecursiveMissCacheD, cacheP) + if _, err := os.Stat(cacheP); err != nil { + u, err := url.Parse(WebhookOnRecursiveMiss) + if err != nil { + return fmt.Errorf("WebhookOnRecursiveMiss (%s) invalid: %w", WebhookOnRecursiveMiss, err) + } + q := u.Query() + q.Set("p", p) + u.RawQuery = q.Encode() + resp, err := http.Post(u.String(), "text/plain", strings.NewReader(p)) + if err != nil { + return fmt.Errorf("failed to call %s for missing %s: %w", WebhookOnRecursiveMiss, p, err) + } + defer resp.Body.Close() + defer io.Copy(io.Discard, resp.Body) + if resp.StatusCode > 250 { + return fmt.Errorf("unexpected status code from %s for %s: %w", WebhookOnRecursiveMiss, p, err) + } + + os.MkdirAll(path.Dir(cacheP), os.ModePerm) + ioutil.WriteFile(cacheP, []byte{}, os.ModePerm) + } + } } else if err := func() error { y, err := NewYaml(path.Join(d, YamlFile)) if err != nil { diff --git a/main_test.go b/main_test.go index d3d94cc..5322abe 100644 --- a/main_test.go +++ b/main_test.go @@ -3,6 +3,8 @@ package main_test import ( "context" "io/ioutil" + "net/http" + "net/http/httptest" "os" "path" "slices" @@ -149,6 +151,38 @@ func TestRunWith(t *testing.T) { } func TestRecursive(t *testing.T) { + webhooks := []string{} + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Query().Get("p") == "" { + t.Errorf("webhook wasn't called with ?p: %s", r.URL.String()) + } + b, _ := ioutil.ReadAll(r.Body) + if string(b) != r.URL.Query().Get("p") { + t.Errorf("webhook wasn't called with ?p == {body}: %q vs %q", r.URL.Query().Get("p"), b) + } + webhooks = append(webhooks, string(b)) + })) + t.Cleanup(s.Close) + t.Cleanup(func() { + t.Logf("webhooks: %+v", webhooks) + if len(webhooks) == 0 { + t.Errorf("expected webhook calls but got none") + } + deduped := slices.Clone(webhooks) + slices.Sort(deduped) + slices.Compact(deduped) + if len(deduped) != len(webhooks) { + t.Errorf("expected no duplicate webhooks but got %+v", webhooks) + } + }) + + main.WebhookOnRecursiveMiss = s.URL + main.WebhookOnRecursiveMissCacheD = t.TempDir() + t.Cleanup(func() { + main.WebhookOnRecursiveMiss = "" + main.WebhookOnRecursiveMissCacheD = "" + }) + was, _ := os.Getwd() t.Cleanup(func() { os.Chdir(was) }) os.Chdir(t.TempDir()) @@ -196,6 +230,8 @@ func TestRecursive(t *testing.T) { if err := main.Recursive(context.Background()); err != nil { t.Fatal(err) + } else if err := main.Recursive(context.Background()); err != nil { + t.Fatalf("failed second run: %v", err) } exists(t, path.Join(outd, "A", "A_SAEA.a"))