diff --git a/.claude/skills/release_strix/SKILL.md b/.claude/skills/release_strix/SKILL.md index 5cf4a86..4962b9c 100644 --- a/.claude/skills/release_strix/SKILL.md +++ b/.claude/skills/release_strix/SKILL.md @@ -1,6 +1,6 @@ --- name: release_strix -description: Full release of Strix -- merge develop to main, tag, build multiarch Docker image, push to Docker Hub, update hassio-strix, create GitHub Release. +description: Full release of Strix -- merge develop to main, tag, build multiarch Docker image, build static binaries, push to Docker Hub, update hassio-strix, create GitHub Release with binaries attached. disable-model-invocation: true --- @@ -13,6 +13,10 @@ You are performing a full release of Strix. Follow every step exactly. Do NOT sk - Strix: `/home/user/Strix` - hassio-strix: `/home/user/hassio-strix` +## Versioning + +Version is injected at build time via ldflags (`-X main.version=$VERSION`). There is NO hardcoded version in the source code -- `main.go` has `var version = "dev"` as default. The Dockerfile passes `--build-arg VERSION` which maps to the same ldflags. Do NOT edit `main.go` to set the version. + ## Step 1: Gather information ```bash @@ -64,7 +68,7 @@ ls -lh cameras.db ```bash cd /home/user/Strix go test ./... -go build ./... +CGO_ENABLED=0 go build ./... ``` If tests or build fail -- STOP and report the error. Do not continue. @@ -157,17 +161,40 @@ git commit -m "Release v$VERSION" git push origin main ``` -## Step 11: GitHub Release +## Step 11: Build static binaries + +Build static binaries for both platforms: + +```bash +cd /home/user/Strix +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.version=$VERSION" -o strix-linux-amd64 . +CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X main.version=$VERSION" -o strix-linux-arm64 . +``` + +Verify both binaries are statically linked: +```bash +file strix-linux-amd64 strix-linux-arm64 +``` + +## Step 12: GitHub Release + +Create the release and attach binaries: ```bash cd /home/user/Strix PREV_TAG=$(git tag --sort=-version:refname | sed -n '2p') gh release create v$VERSION \ --title "v$VERSION" \ - --notes "$(git log --oneline ${PREV_TAG}..v$VERSION)" + --notes "$(git log --oneline ${PREV_TAG}..v$VERSION)" \ + strix-linux-amd64 strix-linux-arm64 ``` -## Step 12: Final report +Clean up binaries after upload: +```bash +rm -f strix-linux-amd64 strix-linux-arm64 +``` + +## Step 13: Final report Output a summary: @@ -176,6 +203,7 @@ Release v$VERSION complete: - Git: tag v$VERSION pushed to main - Docker Hub: eduard256/strix:$VERSION (amd64 + arm64) - Health check: version "$VERSION" verified +- Binaries: strix-linux-amd64, strix-linux-arm64 attached to GitHub Release - hassio-strix: config.json updated to $VERSION, pushed to main - GitHub Release: ``` diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2f86588 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +## [2.0.0] - 2025-04-05 + +### Added +- Complete rewrite as a single Go binary with modular architecture +- DVRIP protocol support +- RTMP protocol support +- Bubble protocol support +- HTTP/HTTPS protocol support for snapshots and streams +- Direct stream URL input in web UI +- Frigate config proxy with auto-discovery via HA Supervisor API +- Frigate connectivity check endpoint +- go2rtc module with auto-discovery +- Network probe system: port scanning, ICMP ping, ARP/OUI lookup, mDNS/HomeKit detection, HTTP probing +- Camera stream tester with automatic JPEG screenshot capture and resolution extraction +- Frigate config generator from camera database +- Web UI pages: search, test, config, URLs, go2rtc streams, HomeKit +- SQLite camera database loaded from external StrixCamDB repository +- Universal Linux installer script with Docker/Compose auto-setup +- In-memory log viewer API endpoint +- Dockerfile with multi-stage build and healthcheck + +### Fixed +- Screenshot URL path: removed leading slash +- Credentials with special characters are now URL-encoded in stream URLs +- Credentials no longer leak in debug logs + +### Changed +- Version is now injected at build time via ldflags (no hardcoded version in source) +- Pure Go build with no CGO dependency (switched from mattn/go-sqlite3 to modernc.org/sqlite) +- Port is always included in URL for protocols with raw TCP dial +- Structured logging with zerolog, separate from human-readable output diff --git a/Dockerfile b/Dockerfile index cfb72ac..beb6b47 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,5 @@ FROM golang:1.26-alpine AS builder -RUN apk add --no-cache gcc musl-dev - WORKDIR /src COPY go.mod go.sum ./ RUN go mod download @@ -9,7 +7,7 @@ RUN go mod download COPY . . ARG VERSION=dev -RUN CGO_ENABLED=1 go build -ldflags "-s -w -X main.version=${VERSION}" -o /strix . +RUN CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}" -o /strix . FROM alpine:latest diff --git a/go.mod b/go.mod index d2aa909..16f15c7 100644 --- a/go.mod +++ b/go.mod @@ -4,23 +4,30 @@ go 1.26 require ( github.com/AlexxIT/go2rtc v1.9.14 - github.com/mattn/go-sqlite3 v1.14.37 github.com/miekg/dns v1.1.72 github.com/rs/zerolog v1.34.0 + modernc.org/sqlite v1.48.1 ) require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/ncruces/go-strftime v1.0.0 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.16 // indirect github.com/pion/rtp v1.10.0 // indirect github.com/pion/sdp/v3 v3.0.17 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 // indirect github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f // indirect - golang.org/x/mod v0.32.0 // indirect - golang.org/x/net v0.49.0 // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/net v0.50.0 // indirect golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/tools v0.41.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/tools v0.42.0 // indirect + modernc.org/libc v1.70.0 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.11.0 // indirect ) diff --git a/go.sum b/go.sum index 6895456..b202810 100644 --- a/go.sum +++ b/go.sum @@ -3,9 +3,17 @@ github.com/AlexxIT/go2rtc v1.9.14/go.mod h1:fcN11KXBbIcExYRqMVDlW7amLZ4/z+hpr5+3 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -13,10 +21,10 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.37 h1:3DOZp4cXis1cUIpCfXLtmlGolNLp2VEqhiB/PARNBIg= -github.com/mattn/go-sqlite3 v1.14.37/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo= @@ -28,6 +36,8 @@ github.com/pion/sdp/v3 v3.0.17/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= @@ -37,18 +47,46 @@ github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f h1:1R9KdKjCNSd7F8iGTxI github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f/go.mod h1:vQhwQ4meQEDfahT5kd61wLAF5AAeh5ZPLVI4JJ/tYo8= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= -golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= +golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= +modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw= +modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0= +modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= +modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= +modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= +modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw= +modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.48.1 h1:S85iToyU6cgeojybE2XJlSbcsvcWkQ6qqNXJHtW5hWA= +modernc.org/sqlite v1.48.1/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/internal/probe/probe.go b/internal/probe/probe.go index 109b789..057ce9e 100644 --- a/internal/probe/probe.go +++ b/internal/probe/probe.go @@ -13,7 +13,7 @@ import ( "github.com/eduard256/strix/pkg/probe" "github.com/rs/zerolog" - _ "github.com/mattn/go-sqlite3" + _ "modernc.org/sqlite" ) const probeTimeout = 100 * time.Millisecond @@ -29,7 +29,7 @@ func Init() { log = app.GetLogger("probe") var err error - db, err = sql.Open("sqlite3", "file:"+app.DB+"?mode=ro&immutable=1") + db, err = sql.Open("sqlite", "file:"+app.DB+"?mode=ro&immutable=1") if err != nil { log.Error().Err(err).Msg("[probe] db open") } diff --git a/internal/search/search.go b/internal/search/search.go index 732dd18..68a81f5 100644 --- a/internal/search/search.go +++ b/internal/search/search.go @@ -11,7 +11,7 @@ import ( "github.com/eduard256/strix/pkg/camdb" "github.com/rs/zerolog" - _ "github.com/mattn/go-sqlite3" + _ "modernc.org/sqlite" ) var log zerolog.Logger @@ -21,7 +21,7 @@ func Init() { log = app.GetLogger("search") var err error - db, err = sql.Open("sqlite3", "file:"+app.DB+"?mode=ro&immutable=1") + db, err = sql.Open("sqlite", "file:"+app.DB+"?mode=ro&immutable=1") if err != nil { log.Fatal().Err(err).Msg("[search] db open") } diff --git a/main.go b/main.go index 0b17abe..882d091 100644 --- a/main.go +++ b/main.go @@ -11,8 +11,13 @@ import ( "github.com/eduard256/strix/internal/test" ) +// version is set at build time via ldflags: +// +// go build -ldflags "-X main.version=2.0.0" +var version = "dev" + func main() { - app.Version = "2.0.0" + app.Version = version type module struct { name string