5 Commits

Author SHA1 Message Date
user
cf825a7260 consolidate appname to internal/globals as a constant
All checks were successful
check / check (push) Successful in 1m47s
The appname was redundantly defined in both cmd/pixad/main.go and
internal/globals/globals.go. Since it is always "pixad" and is not
actually set via ldflags (only Version is), define it once as an
unexported constant in globals.go and remove it from main.go.
2026-03-17 01:53:21 -07:00
2e934c8894 fix: QA audit fixes for 1.0/MVP readiness (#25)
All checks were successful
check / check (push) Successful in 5s
closes #24

## QA Audit Fixes

This PR addresses issues found during the 1.0/MVP QA audit.

### Changes

1. **TODO.md: Mark AVIF encoding as done** — AVIF encoding is fully implemented via govips in `processor.go` but was still listed as a TODO item.

2. **scripts/manual-test.sh: Fix form field names** — The manual test script was using wrong field names:
   - Login form: was sending `password=...`, should be `key=...` (matching the HTML form's `name="key"`)
   - Generator form: was sending `source_url`, `fit_mode` — should be `url`, `fit` (matching the handler's `r.FormValue()` calls)
   - This means **the manual test script never actually worked** — login always failed silently because the `key` field was empty.

### Full QA Audit Results

The comprehensive QA audit report has been posted as a comment on [issue #24](#24).

Co-authored-by: user <user@Mac.lan guest wan>
Reviewed-on: #25
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-03-15 17:58:13 +01:00
2f15340f26 Split Dockerfile: pre-built golangci-lint stage for faster CI (#23)
All checks were successful
check / check (push) Successful in 5s
## Summary

Splits the Dockerfile into a dedicated lint stage using the pre-built `golangci/golangci-lint:v2.10.1-alpine` Docker image, replacing the manual binary download with curl/sha256 verification.

## Changes

- **Lint stage** (`AS lint`): Uses `golangci/golangci-lint:v2.10.1-alpine` pinned by sha256. Runs `make fmt-check` + `make lint`. Includes CGO deps (`build-base`, `vips-dev`, `libheif-dev`, `pkgconfig`) needed for type-checking govips imports.
- **Build stage** (`AS builder`): Depends on lint stage via `COPY --from=lint /src/go.sum /dev/null`. Runs `make test` + builds the binary. Removes `curl` (no longer needed) and the manual golangci-lint download block.
- **Runtime stage**: Unchanged.

## Benefits

- Eliminates slow multi-arch binary download + sha256 verification step
- Lint and build stages can potentially run in parallel with BuildKit
- Better Docker layer caching — lint deps cached separately from build deps
- All images remain pinned by sha256 with version+date comments

## Verification

- `docker build .` passes: fmt-check , lint (0 issues) , all tests pass , binary builds 

Closes [#18](#18)

<!-- session: agent:sdlc-manager:subagent:7aac9c54-81c8-4494-94ab-0843f97a1e62 -->

Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de>
Reviewed-on: #23
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-03-02 21:09:51 +01:00
811c210b09 Merge pull request 'fix: Docker build failures on arm64 (closes #15)' (#16) from fix/docker-multiarch-lint into main
All checks were successful
check / check (push) Successful in 6s
Reviewed-on: #16
2026-02-25 20:51:44 +01:00
clawbot
5ca64a37ce fix: detect architecture for golangci-lint download in Docker build
All checks were successful
check / check (push) Successful in 1m34s
The golangci-lint binary was hardcoded as linux-amd64, causing Docker builds
to fail on arm64 hosts. The amd64 ELF binary cannot execute on aarch64,
producing a misleading shell syntax error during make check.

Use uname -m to detect the container architecture at build time and download
the matching binary. Both amd64 and arm64 SHA-256 hashes are pinned.

Closes #15
2026-02-25 06:12:47 -08:00
5 changed files with 38 additions and 29 deletions

View File

@@ -1,7 +1,29 @@
# Lint stage
# golangci/golangci-lint:v2.10.1-alpine, 2026-02-17
FROM golangci/golangci-lint:v2.10.1-alpine@sha256:33bc6b6156d4c7da87175f187090019769903d04dd408833b83083ed214b0ddf AS lint
RUN apk add --no-cache make build-base vips-dev libheif-dev pkgconfig
WORKDIR /src
# Copy go mod files first for better layer caching
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Run formatting check and linter
RUN make fmt-check
RUN make lint
# Build stage
# golang:1.25.4-alpine, 2026-02-25
FROM golang:1.25.4-alpine@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS builder
# Depend on lint stage passing
COPY --from=lint /src/go.sum /dev/null
ARG VERSION=dev
# Install build dependencies for CGO image libraries
@@ -9,15 +31,7 @@ RUN apk add --no-cache \
build-base \
vips-dev \
libheif-dev \
pkgconfig \
curl
# golangci-lint v2.10.1, 2026-02-25
RUN curl -sSfL https://github.com/golangci/golangci-lint/releases/download/v2.10.1/golangci-lint-2.10.1-linux-amd64.tar.gz -o /tmp/golangci-lint.tar.gz && \
echo "dfa775874cf0561b404a02a8f4481fc69b28091da95aa697259820d429b09c99 /tmp/golangci-lint.tar.gz" | sha256sum -c - && \
tar -xzf /tmp/golangci-lint.tar.gz -C /tmp && \
mv /tmp/golangci-lint-2.10.1-linux-amd64/golangci-lint /usr/local/bin/ && \
rm -rf /tmp/golangci-lint*
pkgconfig
WORKDIR /src
@@ -28,8 +42,8 @@ RUN GOTOOLCHAIN=auto go mod download
# Copy source code
COPY . .
# Run all checks (fmt-check, lint, test)
RUN make check
# Run tests
RUN make test
# Build with CGO enabled
RUN CGO_ENABLED=1 GOTOOLCHAIN=auto go build -ldflags "-X main.Version=${VERSION}" -o /pixad ./cmd/pixad

View File

@@ -6,7 +6,7 @@ Remaining tasks sorted by priority for a working 1.0 release.
### Image Processing
- [x] Add WebP encoding support (currently returns error)
- [ ] Add AVIF encoding support (currently returns error)
- [x] Add AVIF encoding support (implemented via govips)
### Manual Testing (verify auth/encrypted URLs work)
- [ ] Manual test: visit `/`, see login form

View File

@@ -17,10 +17,7 @@ import (
"sneak.berlin/go/pixa/internal/server"
)
var (
Appname = "pixad" //nolint:gochecknoglobals // set by ldflags
Version string //nolint:gochecknoglobals // set by ldflags
)
var Version string //nolint:gochecknoglobals // set by ldflags
var configPath string //nolint:gochecknoglobals // cobra flag
@@ -40,7 +37,6 @@ func main() {
}
func run(_ *cobra.Command, _ []string) {
globals.Appname = Appname
globals.Version = Version
// Set config path in environment if specified via flag

View File

@@ -5,11 +5,10 @@ import (
"go.uber.org/fx"
)
// Build-time variables populated from main() via ldflags.
var (
Appname string //nolint:gochecknoglobals // set from main
Version string //nolint:gochecknoglobals // set from main
)
const appname = "pixad"
// Version is populated from main() via ldflags.
var Version string //nolint:gochecknoglobals // set from main
// Globals holds application-wide constants.
type Globals struct {
@@ -20,7 +19,7 @@ type Globals struct {
// New creates a new Globals instance from build-time variables.
func New(_ fx.Lifecycle) (*Globals, error) {
return &Globals{
Appname: Appname,
Appname: appname,
Version: Version,
}, nil
}

View File

@@ -48,7 +48,7 @@ fi
# Test 3: Wrong password shows error
echo "--- Test 3: Login with wrong password ---"
WRONG_LOGIN=$(curl -sf -X POST "$BASE_URL/" -d "password=wrong-key" -c "$COOKIE_JAR")
WRONG_LOGIN=$(curl -sf -X POST "$BASE_URL/" -d "key=wrong-key" -c "$COOKIE_JAR")
if echo "$WRONG_LOGIN" | grep -qi "invalid\|error\|incorrect\|wrong"; then
pass "Wrong password shows error message"
else
@@ -57,7 +57,7 @@ fi
# Test 4: Correct password redirects to generator
echo "--- Test 4: Login with correct signing key ---"
curl -sf -X POST "$BASE_URL/" -d "password=$SIGNING_KEY" -c "$COOKIE_JAR" -b "$COOKIE_JAR" -L -o /dev/null
curl -sf -X POST "$BASE_URL/" -d "key=$SIGNING_KEY" -c "$COOKIE_JAR" -b "$COOKIE_JAR" -L -o /dev/null
GENERATOR_PAGE=$(curl -sf "$BASE_URL/" -b "$COOKIE_JAR")
if echo "$GENERATOR_PAGE" | grep -qi "generate\|url\|source\|logout"; then
pass "Correct password shows generator page"
@@ -68,12 +68,12 @@ fi
# Test 5: Generate encrypted URL
echo "--- Test 5: Generate encrypted URL ---"
GEN_RESULT=$(curl -sf -X POST "$BASE_URL/generate" -b "$COOKIE_JAR" \
-d "source_url=$TEST_IMAGE_URL" \
-d "url=$TEST_IMAGE_URL" \
-d "width=800" \
-d "height=600" \
-d "format=jpeg" \
-d "quality=85" \
-d "fit_mode=cover" \
-d "fit=cover" \
-d "ttl=3600")
if echo "$GEN_RESULT" | grep -q "/v1/e/"; then
pass "Encrypted URL generated"
@@ -121,10 +121,10 @@ fi
# Test 9: Generate short-TTL URL and verify expiration
echo "--- Test 9: Expired URL returns 410 ---"
# Login again
curl -sf -X POST "$BASE_URL/" -d "password=$SIGNING_KEY" -c "$COOKIE_JAR" -b "$COOKIE_JAR" -L -o /dev/null
curl -sf -X POST "$BASE_URL/" -d "key=$SIGNING_KEY" -c "$COOKIE_JAR" -b "$COOKIE_JAR" -L -o /dev/null
# Generate URL with 1 second TTL
GEN_RESULT=$(curl -sf -X POST "$BASE_URL/generate" -b "$COOKIE_JAR" \
-d "source_url=$TEST_IMAGE_URL" \
-d "url=$TEST_IMAGE_URL" \
-d "width=100" \
-d "height=100" \
-d "format=jpeg" \