From 866cd4e5804528249b46dcb7a37f4fff436b0301 Mon Sep 17 00:00:00 2001 From: Nat Welch Date: Sat, 26 Apr 2025 19:15:25 -0400 Subject: [PATCH] Github Repo Updates - Upgrades Docker Go to 1.24 - Fixes lint errors - Adds github docker build and push workflow - Adds github golang lint workflow - Exposes port in Dockerfile - Adds gitignore file --- .github/workflows/docker.yml | 47 +++++++++++++++++++++++++++++ .github/workflows/golangci-lint.yml | 23 ++++++++++++++ .gitignore | 1 + Dockerfile | 2 +- anilist.go | 20 ++++++++---- main.go | 27 +++++++++++++---- mal.go | 8 +++-- 7 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .gitignore diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..73fe569 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,47 @@ +name: Create and publish Docker image +on: + push: + branches: + - main + pull_request: +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + attestations: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + - name: Build and push Docker image + id: push + uses: docker/build-push-action@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + context: . + push: ${{ github.ref == 'refs/heads/main' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v2 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: ${{ github.ref == 'refs/heads/main' }} diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..5437097 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,23 @@ +name: golangci-lint +on: + push: + tags: + - v* + branches: + - master + - main + pull_request: +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - name: golangci-lint + uses: golangci/golangci-lint-action@v7 + with: + version: latest + args: -E bodyclose,misspell,gosec,goconst,errorlint \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7996692 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +sonarr-anime-importer diff --git a/Dockerfile b/Dockerfile index 9ada7f4..f60f381 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ## syntax=docker/dockerfile:1 -FROM golang:1.23 +FROM golang:1.24 WORKDIR /app COPY go.mod go.sum ./ RUN go mod download diff --git a/anilist.go b/anilist.go index c5c7346..47017e5 100644 --- a/anilist.go +++ b/anilist.go @@ -102,9 +102,13 @@ func handleAniListAnimeSearch(idMap *ConcurrentMap, permaSkipIds []string) http. search, err := getAniListAnimeSearch(idMap, permaSkipIds, r) if err != nil { w.WriteHeader(500) - w.Write([]byte(err.Error())) + if _, writeErr := w.Write([]byte(err.Error())); writeErr != nil { + log.Printf("Error writing error response: %v", writeErr) + } } else { - w.Write(search) + if _, writeErr := w.Write(search); writeErr != nil { + log.Printf("Error writing response: %v", writeErr) + } } } } @@ -187,7 +191,7 @@ func makeAniListApiCall(q url.Values) (*AniListApiResponse, error) { // Build the GraphQL request body variables := BuildGraphQLVariables(q) - body := map[string]interface{}{ + body := map[string]any{ "query": anilistQuery, "variables": variables, } @@ -201,7 +205,11 @@ func makeAniListApiCall(q url.Values) (*AniListApiResponse, error) { if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { + if closeErr := resp.Body.Close(); closeErr != nil { + log.Printf("Error closing response body: %v", closeErr) + } + }() respData := new(AniListApiResponse) err = json.NewDecoder(resp.Body).Decode(respData) @@ -212,8 +220,8 @@ func makeAniListApiCall(q url.Values) (*AniListApiResponse, error) { } // BuildGraphQLVariables converts URL query parameters into a GraphQL variables map. -func BuildGraphQLVariables(params url.Values) map[string]interface{} { - vars := make(map[string]interface{}) +func BuildGraphQLVariables(params url.Values) map[string]any { + vars := make(map[string]any) // Helper to convert comma-separated strings into slices parseList := func(key string) []string { diff --git a/main.go b/main.go index 977bad1..06c9403 100644 --- a/main.go +++ b/main.go @@ -20,11 +20,13 @@ type ResponseItem struct { AniListId int `json:"anilistId,omitempty"` TvdbId int `json:"tvdbId"` } + type AnimeEntry struct { - TvdbId int `json:"tvdb_id"` - MalId interface{} `json:"mal_id"` - AniListId int `json:"anilist_id"` + TvdbId int `json:"tvdb_id"` + MalId any `json:"mal_id"` + AniListId int `json:"anilist_id"` } + type ConcurrentMap struct { mal map[int]int mut sync.RWMutex @@ -38,8 +40,10 @@ func (m *ConcurrentMap) GetByMalId(i int) int { var lastBuiltAnimeIdList time.Time +const Version = "v0.2.1" + func main() { - log.Println("sonarr-anime-importer v0.2.1") + log.Printf("sonarr-anime-importer %s", Version) log.Println("Building Anime ID Associations...") var idMap = new(ConcurrentMap) buildIdMap(idMap) @@ -57,7 +61,14 @@ func main() { http.HandleFunc("/v1/mal/anime", loggerMiddleware(buildIdMapMiddleware(handleMalAnimeSearch(idMap, permaSkipMalIds)))) http.HandleFunc("/v1/anilist/anime", loggerMiddleware(buildIdMapMiddleware(handleAniListAnimeSearch(idMap, permaSkipAniListIds)))) log.Println("Listening on :3333") - log.Fatal(http.ListenAndServe(":3333", nil)) + + srv := &http.Server{ + Addr: ":3333", + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 120 * time.Second, + } + log.Fatal(srv.ListenAndServe()) } func buildIdMap(idMap *ConcurrentMap) { @@ -69,7 +80,11 @@ func buildIdMap(idMap *ConcurrentMap) { if err != nil { log.Fatal("Error fetching anime_ids.json: ", err) } - defer resp.Body.Close() + defer func() { + if closeErr := resp.Body.Close(); closeErr != nil { + log.Printf("Error closing response body: %v", closeErr) + } + }() idListBytes, err = io.ReadAll(resp.Body) if err != nil { log.Fatal("Error reading anime_ids.json: ", err) diff --git a/mal.go b/mal.go index 76b3b6b..a19521b 100644 --- a/mal.go +++ b/mal.go @@ -17,9 +17,13 @@ func handleMalAnimeSearch(idMap *ConcurrentMap, permaSkipMalIds []string) http.H search, err := getJikanAnimeSearch(idMap, permaSkipMalIds, r) if err != nil { w.WriteHeader(500) - w.Write([]byte(err.Error())) + if _, writeErr := w.Write([]byte(err.Error())); writeErr != nil { + log.Printf("Error writing error response: %v", writeErr) + } } else { - w.Write([]byte(search)) + if _, writeErr := w.Write([]byte(search)); writeErr != nil { + log.Printf("Error writing response: %v", writeErr) + } } }) }