feat(cli,ci): admin reset-password + migrate subcommands; test-DB isolation + CI integration job

#321 — `minstrel admin reset-password [-user admin] [-password X]`:
loads config, updates BOTH password_hash (bcrypt) and subsonic_password
(plaintext, for Subsonic t+s) so neither auth path is left stale;
generates+prints a strong password when -password is omitted. Recovers
a locked-out operator without DB surgery. Subcommand dispatch added to
main.go (os.Args switch before flag-parse; server path untouched) plus
a `minstrel migrate` subcommand exposing db.Migrate standalone.

#339 — integration tests no longer truncate the dev DB:
- deploy/initdb creates minstrel_test on a fresh compose volume;
  `make test-integration` ensures it idempotently and points
  MINSTREL_TEST_DATABASE_URL at minstrel_test.
- docker-compose.yml + README updated.

CI integration job (test-go.yml): the prior workflow only ran
`go test -short -race` with no DB, so the entire integration suite
silently t.Skip'd — "CI green" never covered API/db/scanner. New
`integration` job runs the full `go test -race` against an ephemeral
Postgres service, using the act_runner shared-daemon pattern: no
published ports, discover the service container by job-name filter via
the docker socket, reach it by bridge IP, hard exactly-one assertion +
dev-compose-name reject (a wrong target would truncate real data),
TCP-wait, `minstrel migrate`, then test. Fast `test` job kept as the
quick gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 23:36:36 -04:00
parent a7bea43a13
commit 760b4a7c6c
7 changed files with 236 additions and 7 deletions
+16
View File
@@ -29,6 +29,22 @@ import (
)
func main() {
if len(os.Args) > 1 {
switch os.Args[1] {
case "admin":
if err := runAdmin(os.Args[2:]); err != nil {
fmt.Fprintf(os.Stderr, "minstrel admin: %v\n", err)
os.Exit(1)
}
return
case "migrate":
if err := runMigrate(os.Args[2:]); err != nil {
fmt.Fprintf(os.Stderr, "minstrel migrate: %v\n", err)
os.Exit(1)
}
return
}
}
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "minstrel: %v\n", err)
os.Exit(1)