From fc3a43d615ad11bee8706f85c960d85b7cb2dd7f Mon Sep 17 00:00:00 2001 From: Daniel Whelan Date: Tue, 9 Sep 2025 15:35:56 +1000 Subject: [PATCH 1/3] pkg build --- .github/workflows/ci.yml | 156 +++++++++++++++++++++++++++++++++++++++ main.go | 59 ++++++++++++--- 2 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3a43fc1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,156 @@ +name: CI + +on: + push: + branches: [main, master] + tags: ["v*.*.*"] + pull_request: + branches: [main, master] + +# Required to publish to GHCR +permissions: + contents: read + packages: write + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-test: + name: Build and Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install build dependencies (ZeroMQ for CGO) + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends pkg-config libzmq3-dev + + - name: Determine module directory + id: paths + shell: bash + run: | + if [[ -f go.mod ]]; then + echo "mod_dir=." >> "$GITHUB_OUTPUT" + elif [[ -f indexer/go.mod ]]; then + echo "mod_dir=indexer" >> "$GITHUB_OUTPUT" + else + echo "No go.mod found at repo root or indexer/" >&2 + exit 1 + fi + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: ${{ steps.paths.outputs.mod_dir }}/go.mod + cache: true + + - name: Download modules + working-directory: ${{ steps.paths.outputs.mod_dir }} + run: go mod download + + - name: Build + working-directory: ${{ steps.paths.outputs.mod_dir }} + env: + CGO_ENABLED: "1" # required by mattn/go-sqlite3 + run: go build -v ./... + + - name: Test + working-directory: ${{ steps.paths.outputs.mod_dir }} + env: + CGO_ENABLED: "1" + run: go test -v ./... + + docker: + if: startsWith(github.ref, 'refs/tags/v') + name: Build and Push Docker image + needs: build-and-test + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Determine module directory + id: paths + shell: bash + run: | + if [[ -f go.mod ]]; then + echo "mod_dir=." >> "$GITHUB_OUTPUT" + elif [[ -f indexer/go.mod ]]; then + echo "mod_dir=indexer" >> "$GITHUB_OUTPUT" + else + echo "No go.mod found at repo root or indexer/" >&2 + exit 1 + fi + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/${{ github.repository }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Generate Dockerfile (multi-stage, CGO-ready) + run: | + cat > Dockerfile <<'EOF' + # syntax=docker/dockerfile:1.5 + FROM golang:1.21-bookworm AS build + ARG MODULE_DIR=. + WORKDIR /src + # Install build dependencies for CGO (ZeroMQ, pkg-config, toolchain) + RUN apt-get update && apt-get install -y --no-install-recommends build-essential pkg-config libzmq3-dev && rm -rf /var/lib/apt/lists/* + # Copy module files first for better caching + COPY ${MODULE_DIR}/go.mod ${MODULE_DIR}/go.sum ./ + RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download + # Copy source + COPY ${MODULE_DIR}/ ./ + # Build with CGO enabled (needed for sqlite3 and zmq4) + ENV CGO_ENABLED=1 + RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go build -o /out/indexer ./main.go + + # Runtime image with libzmq5 available + FROM debian:bookworm-slim + WORKDIR /app + RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates libzmq5 && rm -rf /var/lib/apt/lists/* + COPY --from=build /out/indexer /app/indexer + EXPOSE 8000 + ENTRYPOINT ["/app/indexer"] + EOF + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + platforms: linux/amd64 + build-args: | + MODULE_DIR=${{ steps.paths.outputs.mod_dir }} + push: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/main.go b/main.go index eb04db0..1984bd1 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,8 @@ import ( "flag" "fmt" "log" + "os" + "strconv" "time" "github.com/dogeorg/doge" @@ -37,17 +39,17 @@ func main() { log.Printf("\n\n[Indexer] starting") var config Config - flag.StringVar(&config.connStr, "dburl", "index.db", "Database connection string") - flag.StringVar(&config.rpcHost, "rpchost", "127.0.0.1", "RPC host") - flag.IntVar(&config.rpcPort, "rpcport", 22555, "RPC port") - flag.StringVar(&config.rpcUser, "rpcuser", "dogecoin", "RPC username") - flag.StringVar(&config.rpcPass, "rpcpass", "dogecoin", "RPC password") - flag.StringVar(&config.zmqHost, "zmqhost", "127.0.0.1", "ZMQ host") - flag.IntVar(&config.zmqPort, "zmqport", 28332, "ZMQ port") - flag.StringVar(&config.bindAPI, "bindapi", "localhost:8000", "API bind address") - flag.StringVar(&config.corsOrigin, "cors-origin", "http://localhost:5173", "CORS allowed origin") - flag.StringVar(&config.chainName, "chain", "mainnet", "Chain Params (mainnet, testnet, regtest)") - flag.Int64Var(&config.startingHeight, "startingheight", 5830000, "Starting Height") + flag.StringVar(&config.connStr, "dburl", getEnv("DBURL", "index.db"), "Database connection string") + flag.StringVar(&config.rpcHost, "rpchost", getEnv("DBURL", "127.0.0.1"), "RPC host") + flag.IntVar(&config.rpcPort, "rpcport", getEnvInt("RPC_PORT", 22555), "RPC port") + flag.StringVar(&config.rpcUser, "rpcuser", getEnv("RPC_USER", "dogecoin"), "RPC username") + flag.StringVar(&config.rpcPass, "rpcpass", getEnv("RPC_PASS", "dogecoin"), "RPC password") + flag.StringVar(&config.zmqHost, "zmqhost", getEnv("ZMQ_HOST", "127.0.0.1"), "ZMQ host") + flag.IntVar(&config.zmqPort, "zmqport", getEnvInt("ZMQ_PORT", 28332), "ZMQ port") + flag.StringVar(&config.bindAPI, "bindapi", getEnv("BIND_API", "localhost:8000"), "API bind address") + flag.StringVar(&config.corsOrigin, "cors-origin", getEnv("CORS_ORIGIN", "http://localhost:5173"), "CORS allowed origin") + flag.StringVar(&config.chainName, "chain", getEnv("CHAIN", "mainnet"), "Chain Params (mainnet, testnet, regtest)") + flag.Int64Var(&config.startingHeight, "startingheight", getEnvInt64("STARTING_HEIGHT", 5830000), "Starting Height") flag.Parse() @@ -121,3 +123,38 @@ func main() { gov.Start().WaitForShutdown() fmt.Println("[Indexer] stopped") } + +func getEnv(key, fallback string) string { + v := os.Getenv(key) + if v != "" { + return v + } + return fallback +} + +func getEnvInt(key string, fallback int) int { + if v := os.Getenv(key); v != "" { + if i, err := strconv.Atoi(v); err == nil { + return i + } + } + return fallback +} + +func getEnvInt64(key string, fallback int64) int64 { + if v := os.Getenv(key); v != "" { + if i, err := strconv.ParseInt(v, 10, 64); err == nil { + return i + } + } + return fallback +} + +func getEnvBool(key string, fallback bool) bool { + if v := os.Getenv(key); v != "" { + if b, err := strconv.ParseBool(v); err == nil { + return b + } + } + return fallback +} From 276a14aa2b36443fd14fc64ca20a7311ae8d13db Mon Sep 17 00:00:00 2001 From: Daniel Whelan Date: Tue, 9 Sep 2025 15:50:53 +1000 Subject: [PATCH 2/3] Check dockerfile into repo --- .github/workflows/ci.yml | 30 ------------------------------ Dockerfile | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 30 deletions(-) create mode 100644 Dockerfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a43fc1..bc1c878 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,36 +111,6 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - - name: Generate Dockerfile (multi-stage, CGO-ready) - run: | - cat > Dockerfile <<'EOF' - # syntax=docker/dockerfile:1.5 - FROM golang:1.21-bookworm AS build - ARG MODULE_DIR=. - WORKDIR /src - # Install build dependencies for CGO (ZeroMQ, pkg-config, toolchain) - RUN apt-get update && apt-get install -y --no-install-recommends build-essential pkg-config libzmq3-dev && rm -rf /var/lib/apt/lists/* - # Copy module files first for better caching - COPY ${MODULE_DIR}/go.mod ${MODULE_DIR}/go.sum ./ - RUN --mount=type=cache,target=/go/pkg/mod \ - go mod download - # Copy source - COPY ${MODULE_DIR}/ ./ - # Build with CGO enabled (needed for sqlite3 and zmq4) - ENV CGO_ENABLED=1 - RUN --mount=type=cache,target=/go/pkg/mod \ - --mount=type=cache,target=/root/.cache/go-build \ - go build -o /out/indexer ./main.go - - # Runtime image with libzmq5 available - FROM debian:bookworm-slim - WORKDIR /app - RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates libzmq5 && rm -rf /var/lib/apt/lists/* - COPY --from=build /out/indexer /app/indexer - EXPOSE 8000 - ENTRYPOINT ["/app/indexer"] - EOF - - name: Build and push uses: docker/build-push-action@v6 with: diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..abc45f4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM golang:1.21-bookworm AS build +ARG MODULE_DIR=. +WORKDIR /src +# Install build dependencies for CGO (ZeroMQ, pkg-config, toolchain) +RUN apt-get update && apt-get install -y --no-install-recommends build-essential pkg-config libzmq3-dev && rm -rf /var/lib/apt/lists/* +# Copy module files first for better caching +COPY ${MODULE_DIR}/go.mod ${MODULE_DIR}/go.sum ./ +RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download +# Copy source +COPY ${MODULE_DIR}/ ./ +# Build with CGO enabled (needed for sqlite3 and zmq4) +ENV CGO_ENABLED=1 +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go build -o /out/indexer ./main.go + +# Runtime image with libzmq5 available +FROM debian:bookworm-slim +WORKDIR /app +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates libzmq5 && rm -rf /var/lib/apt/lists/* +COPY --from=build /out/indexer /app/indexer +EXPOSE 8000 +ENTRYPOINT ["/app/indexer"] From 1c4e3095af244646babeb978ca7c09b6bcbe7c82 Mon Sep 17 00:00:00 2001 From: Daniel Whelan Date: Tue, 9 Sep 2025 15:51:33 +1000 Subject: [PATCH 3/3] env var fix --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 1984bd1..986ccec 100644 --- a/main.go +++ b/main.go @@ -39,8 +39,8 @@ func main() { log.Printf("\n\n[Indexer] starting") var config Config - flag.StringVar(&config.connStr, "dburl", getEnv("DBURL", "index.db"), "Database connection string") - flag.StringVar(&config.rpcHost, "rpchost", getEnv("DBURL", "127.0.0.1"), "RPC host") + flag.StringVar(&config.connStr, "dburl", getEnv("DB_URL", "index.db"), "Database connection string") + flag.StringVar(&config.rpcHost, "rpchost", getEnv("RPC_HOST", "127.0.0.1"), "RPC host") flag.IntVar(&config.rpcPort, "rpcport", getEnvInt("RPC_PORT", 22555), "RPC port") flag.StringVar(&config.rpcUser, "rpcuser", getEnv("RPC_USER", "dogecoin"), "RPC username") flag.StringVar(&config.rpcPass, "rpcpass", getEnv("RPC_PASS", "dogecoin"), "RPC password")