Skip to content

Release 1.0.0 - #45

Merged
AevumDecessus merged 137 commits into
mainfrom
dev
Jul 1, 2026
Merged

Release 1.0.0#45
AevumDecessus merged 137 commits into
mainfrom
dev

Conversation

@AevumDecessus

Copy link
Copy Markdown
Contributor

Release 1.0.0 MVP

This PR merges dev into main and will trigger:

  • Release validation workflow (checks version bump and changelog)
  • Auto-release creation (creates v1.0.0 tag and GitHub release)
  • Prod image build (triggered by the v1.0.0 tag)

Logout link in header via context processor, clears session and redirects home. Also adds /login/<passphrase>/ for direct passphrase login via URL.
Dev image builds on push to dev, prod image builds on push to main or version tags. Images pushed to GHCR as fragforce-read-dev and fragforce-read-prod.
ci: add Docker image build workflows for dev and prod
…very

Replaces HLS/MSE with session-gated Django views for licensed book audio protection. All recordings stored in the same location regardless of licensing; access control handled at the view layer. Simpler than streaming protocols while still preventing casual download and link sharing.
docs: document signed URL delivery and unified audio storage
Authenticated narrators can now view their recordings and browse available books to record. The dashboard shows book metadata, recording counts, public domain/licensed badges, and a Record button per book. Includes a narrator_required decorator that redirects unauthenticated users to /login/.
Consolidates inline <style> blocks from base.html and all page-specific extra_css blocks into static/css/style.css. Standardizes button colors (white text on blue) across all pages and adds STATICFILES_DIRS to settings.
The truthiness check treated 0 as falsy, allowing books with max_narrators=0 to appear in the available list. Use an explicit None check instead.
Display "X/Y recordings" when a book has a max_narrators limit, using a proper None check instead of truthiness.
Narrator dashboard and static CSS
Narrators must confirm all checklist items before proceeding to the recording page. The "physical book ready" item only appears for licensed books or public domain books without full_text available. Submitting without all items checked keeps the narrator on the checklist page.

Also wires the dashboard Record buttons to the preflight flow and adds a placeholder record page.
When a narrator submits the preflight form without checking all items, unchecked items now get a red border and an error message appears above the checklist.
Blue (#4E738B) for headings, links, borders, and badges. Purple (#5C2751) for header and buttons. Gold (#F09B00) for hover states, error highlights, and attention elements.
Brand colors use --color-fragforce-{blue,purple,gold} naming. Layout colors use --color-{bg,surface,text,muted}. No visual changes.
Add narrator pre-flight checklist and apply brand theming
Adds whitenoise middleware so gunicorn serves collected static files directly without needing a separate static file server. The Dockerfile collectstatic step now fails loudly instead of being silenced.
Enables HSTS, secure cookies, and proxy SSL header detection when SECURE_SSL=True (defaults to True when DEBUG=False). Omits SECURE_SSL_REDIRECT since the reverse proxy handles HTTP-to-HTTPS.
Returns 200 with DB status for Docker health checks. Requests from public IPs get a 404 so the endpoint is not exposed externally.
Reuses DB connections for 10 minutes instead of opening/closing per request. Configurable via CONN_MAX_AGE env var.
Adds MEDIA_URL and MEDIA_ROOT settings, serves media via Django in development, and ignores the media directory in git. Direct media URL is only available when DEBUG=True - production serves recordings through gated views.
Deployment hardening: whitenoise, security, health check, media
The build step needs Django to initialize but doesn't need real credentials. Provide a dummy SECRET_KEY and DATABASE_URL so collectstatic can run without a .env file.
Fix collectstatic failing in Docker build
Creates a dedicated app user/group for the runtime process. Adds .dockerignore to prevent .env, .git, and other sensitive files from being copied into the image.
GET-only views use @require_GET, form views use @require_http_methods([GET, POST]). Prevents unintended method handling.
Use queryset annotation with Count() instead of calling book.recordings.count() inside the loop, reducing the number of database queries from O(n) to 1.
The non-root app user has no writable home directory, so uv fails trying to create its cache at /nonexistent/.cache/uv.
Disable uv cache in container
AevumDecessus and others added 23 commits June 29, 2026 14:19
Document Docker-based dev workflow, test commands, project structure, CI workflows, and the dev/prod image split.
Fix playback description (HTML5 audio with byte-range, not HLS/MSE), URL patterns (short_code not nanoid), and infrastructure section.
Fix reliability and maintainability issues
The post_delete receiver in books/signals.py was never connected because the module was not imported during app startup. Add the import in BooksConfig.ready() so the receiver is properly registered.

Add tests covering the delete_recording_file signal handler for all file cleanup paths.
Cover BookAdmin, NarratorAdmin, RecordingAdmin, QRCodeAdmin, EventCodeAdmin, and InviteLinkAdmin including get_exclude, get_fields, custom actions, and display methods.
Cover missing audio_file, missing raw file on disk, QR creation failure resilience, spawn_remux thread behavior, and recover_pending_recordings integration.
Improve test coverage and fix signal registration bug
Add a "Re-record this book" link on the flagged recording detail page. When a narrator uploads a new recording for a book, any previously flagged recordings for that book are deleted. Flagged recordings no longer count toward max_narrators limits.
Cover flagged recording dashboard visibility, max_narrators exclusion of flagged recordings, re-record link presence, and cleanup of flagged recordings on re-upload.
Allow narrators to re-record flagged recordings
Differentiate dev and prod image build job names so they can be individually required in branch rulesets.
Rename image workflow jobs to distinct contexts
Change password generation from 4alphanumeric-word-word to word-word-2digits format (e.g. apple-banana-23 instead of 2a3f-apple-banana). Easier to read and type while maintaining reasonable entropy (1000 * 1000 * 100 = 100M combinations).

Update test to verify words come from the wordlist and digits are numeric.
Move narrator name from password-gated section to book info header so it's visible immediately on the playback page, even when a password is required. This helps users know who recorded the book before unlocking it.

Add test to verify narrator shows before password entry.
UX improvements: simpler passwords and visible narrator attribution
Add comprehensive developer workflow sections:
- Pre-commit checks and PR requirements
- Dependency management with uv
- Database migrations and reset procedures
- Django shell and admin setup
- Debugging tools and log access
- Common development tasks with examples
- Complete environment variable reference with types and defaults
Validates that PRs to main include:
- Version bump in pyproject.toml
- CHANGELOG.md update
- Matching version entry in changelog

All checks run to completion and report results to job summary.
When code is pushed to main:
- Reads version from pyproject.toml
- Creates git tag (v1.0.0)
- Extracts changelog section for that version
- Creates GitHub release with changelog as notes
- Skips if tag already exists
With auto-release workflow creating tags, prod images are now built only when releases are tagged, avoiding duplicate builds on main.
Comment thread .github/workflows/sonar.yaml Dismissed
Comment on lines +10 to +67
name: Validate version and changelog
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0

- name: Fetch base branch
env:
BASE_REF: ${{ github.base_ref }}
run: git fetch origin "$BASE_REF"

- name: Run all validation checks
env:
BASE_REF: ${{ github.base_ref }}
run: |
FAILED=0

echo "### Release Validation" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"

# Check 1: Version bump
BASE_VERSION=$(git show "origin/$BASE_REF:pyproject.toml" | grep '^version = ' | cut -d'"' -f2)
PR_VERSION=$(grep '^version = ' pyproject.toml | cut -d'"' -f2)

if [ "$BASE_VERSION" = "$PR_VERSION" ]; then
echo "❌ Version not bumped in pyproject.toml (still $BASE_VERSION)" >> "$GITHUB_STEP_SUMMARY"
FAILED=1
else
echo "✅ Version bumped from $BASE_VERSION to $PR_VERSION" >> "$GITHUB_STEP_SUMMARY"
fi

# Check 2: CHANGELOG.md updated
if ! git diff --name-only "origin/$BASE_REF...HEAD" | grep -q '^CHANGELOG.md$'; then
echo "❌ CHANGELOG.md not updated" >> "$GITHUB_STEP_SUMMARY"
FAILED=1
else
echo "✅ CHANGELOG.md updated" >> "$GITHUB_STEP_SUMMARY"

# Check 3: Version in changelog (only if version was bumped and changelog updated)
if [ "$BASE_VERSION" != "$PR_VERSION" ]; then
if ! grep -q "^## \[$PR_VERSION\]" CHANGELOG.md; then
echo "❌ Version $PR_VERSION not found in CHANGELOG.md (expected: \`## [$PR_VERSION] - YYYY-MM-DD\`)" >> "$GITHUB_STEP_SUMMARY"
FAILED=1
else
echo "✅ Version $PR_VERSION found in CHANGELOG.md" >> "$GITHUB_STEP_SUMMARY"
fi
fi
fi

echo "" >> "$GITHUB_STEP_SUMMARY"
if [ $FAILED -eq 1 ]; then
echo "**Result:** ❌ Validation failed" >> "$GITHUB_STEP_SUMMARY"
exit 1
else
echo "**Result:** ✅ All checks passed" >> "$GITHUB_STEP_SUMMARY"
fi
Comment thread config/urls.py Outdated
connection.ensure_connection()
return JsonResponse({"status": "ok"})
except Exception as e:
return JsonResponse({"status": "error", "detail": str(e)}, status=503)
Add missing permissions block to validate-release workflow (medium severity).

Remove exception detail from healthz endpoint to prevent information disclosure (medium severity).

Add CodeQL workflow to scan PRs to dev and main for Python, JavaScript, and GitHub Actions.
@sonarqubecloud

sonarqubecloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

@parkervcp parkervcp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@AevumDecessus
AevumDecessus merged commit 4916868 into main Jul 1, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants