Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8c54c37
Upgrade .NET/Node, refactor repos & update CI
therobbiedavis Apr 18, 2026
90127e1
Use repository interfaces instead of DbContext
therobbiedavis Apr 19, 2026
d54e579
Add targeted download queries to repositories
therobbiedavis Apr 19, 2026
92ab409
Add cancellation token, queue stats, map fields
therobbiedavis Apr 19, 2026
f8ac781
Optimize queries, preserve settings, adjust tests
therobbiedavis Apr 19, 2026
ba0803d
Use latestMinor rollForward in global.json
therobbiedavis Apr 19, 2026
308d1a0
Rename nightly workflow to beta and update CI/docs
therobbiedavis Apr 20, 2026
f03f894
Add CODEOWNERS; update WARP and SECURITY docs
therobbiedavis Apr 20, 2026
4a4c38e
Making repo AGPLv3.0 compliant
therobbiedavis Apr 20, 2026
fec466b
Replace block license headers with HTML comments
therobbiedavis Apr 20, 2026
1b344d5
PR review changes
therobbiedavis Apr 21, 2026
78ed9a7
PR Review #2
therobbiedavis Apr 21, 2026
017863c
CI: label-driven canary, beta include short SHA
therobbiedavis Apr 22, 2026
11d188d
Add post-release PR to forward hotfixes to canary
therobbiedavis Apr 22, 2026
a020ec8
Add read permissions to PR label workflow
therobbiedavis Apr 22, 2026
f4b0658
Clarify 409 error comment in libraryImport
therobbiedavis Apr 22, 2026
4087376
Add project .editorconfig and mark fe root
therobbiedavis Apr 22, 2026
024eb65
Merge branch 'canary' into chore/dependencies-and-workflow
therobbiedavis Apr 22, 2026
63140d7
Introduce reusable build-and-publish workflow
therobbiedavis Apr 22, 2026
25ff557
Update editorconfig and logging overrides
therobbiedavis Apr 22, 2026
641e277
Rename *_repo fields to *_repository
therobbiedavis Apr 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
45 changes: 45 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
root = true

# ─── Defaults ────────────────────────────────────────────────────────────────
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

# ─── C# ───────────────────────────────────────────────────────────────────────
[*.cs]
indent_style = space
indent_size = 4

# ─── XML / project files ──────────────────────────────────────────────────────
[*.{csproj,props,targets,slnx,xml,config,resx}]
indent_style = space
indent_size = 2

# ─── Web / frontend (deferred to fe/.editorconfig) ────────────────────────────
# fe/.editorconfig is root = true and owns all JS/TS/Vue/CSS files under fe/

# ─── YAML ─────────────────────────────────────────────────────────────────────
[*.{yml,yaml}]
indent_style = space
indent_size = 2

# ─── JSON ─────────────────────────────────────────────────────────────────────
[*.json]
indent_style = space
indent_size = 2

# ─── Markdown ─────────────────────────────────────────────────────────────────
[*.md]
trim_trailing_whitespace = true

# ─── Shell scripts ────────────────────────────────────────────────────────────
[*.sh]
end_of_line = lf

# ─── Windows batch / PowerShell ───────────────────────────────────────────────
[*.{bat,cmd,ps1}]
end_of_line = crlf
40 changes: 40 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# All PRs require review from a maintainer.
# Maintainers are org members of the listenarrs/maintainers team.
* @listenarrs/maintainers

# CI/CD workflows — any change requires maintainer sign-off
.github/workflows/ @listenarrs/maintainers
.github/CODEOWNERS @listenarrs/maintainers
.github/dependabot.yml @listenarrs/maintainers

# Security and legal
SECURITY.md @listenarrs/maintainers
LICENSE @listenarrs/maintainers

# Docker and deployment
Dockerfile @listenarrs/maintainers
docker-entrypoint.sh @listenarrs/maintainers
docker-compose.yml @listenarrs/maintainers
listenarr.api/Dockerfile.runtime @listenarrs/maintainers

# .NET project and build configuration — controls SDK, versions, global deps
global.json @listenarrs/maintainers
Directory.Build.props @listenarrs/maintainers
Directory.Packages.props @listenarrs/maintainers
listenarr.api/Listenarr.Api.csproj @listenarrs/maintainers

# Database migrations — schema changes are hard to reverse
listenarr.infrastructure/Migrations/ @listenarrs/maintainers

# Application entry point and DI wiring
listenarr.api/Program.cs @listenarrs/maintainers

# Default configuration shipped with the app
listenarr.api/config/appsettings/ @listenarrs/maintainers

# Dependency manifests — watch for supply-chain changes
package.json @listenarrs/maintainers
package-lock.json @listenarrs/maintainers
fe/package.json @listenarrs/maintainers
fe/package-lock.json @listenarrs/maintainers
listenarr.api/tools/discord-bot/package.json @listenarrs/maintainers
4 changes: 2 additions & 2 deletions .github/COPYRIGHT_HEADER.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ These copyright headers should be added to the top of source files in the Listen
```csharp
/*
* Listenarr - Audiobook Management System
* Copyright (C) 2024-2025 Robbie Davis
* Copyright (C) 2024-2026 Listenarr Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
Expand All @@ -29,7 +29,7 @@ These copyright headers should be added to the top of source files in the Listen
```typescript
/*
* Listenarr - Audiobook Management System
* Copyright (C) 2024-2025 Robbie Davis
* Copyright (C) 2024-2026 Listenarr Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
Expand Down
7 changes: 4 additions & 3 deletions .github/WARP.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,16 +224,17 @@ This produces a single deployment artifact containing both backend and frontend.
5. Images cached in `listenarr.api/wwwroot/cache/` (gitignored)

### Production Deployment
- Docker images available at `listenarrs/listenarr` (latest, stable, nightly tags)
- Docker images available at `listenarrs/listenarr` (latest, stable, canary, beta tags)
- Self-contained executables for Windows, Linux, macOS
- Single-container deployment includes both API and frontend
- Persistent volume at `/app/config` for database and configuration

### Version Management
- Version controlled in `listenarr.api/Listenarr.Api.csproj`
- CI automatically increments versions:
- Nightly builds: patch increment (1.2.3 → 1.2.4)
- Release builds: minor increment + patch reset (1.2.3 → 1.3.0)
- Canary builds (`canary` branch): patch increment (1.2.3 → 1.2.4), tagged `v1.2.4-canary`
- Beta builds (`beta` branch): minor increment + patch reset (1.2.3 → 1.3.0), tagged `v1.3.0-beta`
- Release builds (version tag): major increment + reset (1.2.3 → 2.0.0)

## Important Development Notes

Expand Down
143 changes: 143 additions & 0 deletions .github/workflows/beta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
name: Beta — build executables & Docker (beta)

concurrency:
group: beta-build-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: write
pull-requests: write
packages: write

on:
Comment thread
T4g1 marked this conversation as resolved.
push:
branches: [ beta ]

jobs:
beta-build:
if: github.actor != 'github-actions[bot]'
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve-version.outputs.VERSION }}
short_sha: ${{ steps.short-sha.outputs.SHORT_SHA }}
pr_number: ${{ steps.find_triggering_pr.outputs.PR_NUMBER }}
pr_title: ${{ steps.find_triggering_pr.outputs.PR_TITLE }}
pr_body_raw: ${{ steps.find_triggering_pr.outputs.PR_BODY_RAW }}
env:
API_PROJECT: listenarr.api/Listenarr.Api.csproj

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24

- name: Resolve target version
id: resolve-version
run: |
set -euo pipefail
# Beta is a stabilisation snapshot — version is whatever canary last set.
VERSION=$(sed -n "s:.*<Version>\(.*\)</Version>.*:\1:p" "${{ env.API_PROJECT }}" 2>/dev/null | head -n1)
if [ -z "${VERSION}" ]; then
VERSION=$(node -p "require('./fe/package.json').version" 2>/dev/null || echo "0.0.0")
fi
echo "Beta version (from canary): ${VERSION}"
echo "VERSION=${VERSION}" >> "$GITHUB_OUTPUT"

- name: Compute short SHA
id: short-sha
run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"

- name: Find triggering Pull Request
id: find_triggering_pr
run: |
PR_DATA=$(gh api graphql -f query='
query($owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
pullRequests(first: 20, states: MERGED, baseRefName: "beta", orderBy: {field: UPDATED_AT, direction: DESC}) {
edges {
node {
number
title
body
baseRefName
mergeCommit { oid }
labels(first: 10) { nodes { name } }
}
}
}
}
}' -f owner='${{ github.repository_owner }}' -f repo='${{ github.event.repository.name }}')

FILTERED_PR=$(echo "$PR_DATA" | jq -r '
[.data.repository.pullRequests.edges[] |
select(
(.node.title | test("[Bb]ump version|[Vv]ersion bump|\\[skip ci\\]"; "i") | not) and
(.node.labels.nodes | map(.name) | any(. == "version-bump" or . == "automated") | not)
) |
.node |
{number, title, body, baseRefName}][0] // null
')

echo "Recent PRs found:"
echo "$PR_DATA" | jq -r '.data.repository.pullRequests.edges[:5][] | "PR #\(.node.number): \(.node.title)"'

if [ "$FILTERED_PR" != "" ] && [ "$FILTERED_PR" != "null" ]; then
PR_NUMBER=$(echo "$FILTERED_PR" | jq -r '.number // ""')
PR_TITLE=$(echo "$FILTERED_PR" | jq -r '.title // ""')
PR_BODY_RAW=$(echo "$FILTERED_PR" | jq -r '.body // ""')
echo "✅ Found non-version-bump PR #$PR_NUMBER: $PR_TITLE"
else
echo "⚠️ No non-version-bump PR found, using fallback"
PR_NUMBER=""
PR_TITLE="Beta changes"
PR_BODY_RAW="Recent beta changes merged to beta branch."
fi

echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "PR_TITLE=$PR_TITLE" >> $GITHUB_OUTPUT
echo "PR_BODY_RAW<<EOF_PR_BODY" >> $GITHUB_OUTPUT
echo "$PR_BODY_RAW" >> $GITHUB_OUTPUT
echo "EOF_PR_BODY" >> $GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

beta-publish:
needs: beta-build
uses: ./.github/workflows/build-and-publish.yml
with:
version: ${{ needs.beta-build.outputs.version }}
short_sha: ${{ needs.beta-build.outputs.short_sha }}
channel: beta
artifact_prefix: beta-artifacts
include_osx: false
release_tag: v${{ needs.beta-build.outputs.version }}-beta-${{ needs.beta-build.outputs.short_sha }}
release_name: Beta ${{ needs.beta-build.outputs.version }} (${{ needs.beta-build.outputs.short_sha }})
prerelease: true
pr_number: ${{ needs.beta-build.outputs.pr_number }}
pr_title: ${{ needs.beta-build.outputs.pr_title }}
pr_body_raw: ${{ needs.beta-build.outputs.pr_body_raw }}
discord_text: A new beta build has been released for docker.
docker_tags: |
docker.io/therobbiedavis/listenarr:beta
docker.io/therobbiedavis/listenarr:beta-${{ needs.beta-build.outputs.version }}
docker.io/therobbiedavis/listenarr:beta-${{ needs.beta-build.outputs.version }}-${{ needs.beta-build.outputs.short_sha }}
ghcr.io/listenarrs/listenarr:beta
ghcr.io/listenarrs/listenarr:beta-${{ needs.beta-build.outputs.version }}
ghcr.io/listenarrs/listenarr:beta-${{ needs.beta-build.outputs.version }}-${{ needs.beta-build.outputs.short_sha }}
atcr.io/therobbiedavis.com/listenarr:beta
atcr.io/therobbiedavis.com/listenarr:beta-${{ needs.beta-build.outputs.version }}
atcr.io/therobbiedavis.com/listenarr:beta-${{ needs.beta-build.outputs.version }}-${{ needs.beta-build.outputs.short_sha }}
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
ATCR_USERNAME: ${{ secrets.ATCR_USERNAME }}
ATCR_TOKEN: ${{ secrets.ATCR_TOKEN }}
LISTENARR_DISCORD_WEBHOOK_URL: ${{ secrets.LISTENARR_DISCORD_WEBHOOK_URL }}

# Skipped: separate Web image. The API image contains the frontend in wwwroot.
Loading
Loading