Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
386 commits
Select commit Hold shift + click to select a range
aeb281e
fix(tagging): show the live per-source lookup in the status table
ajslater Aug 27, 2026
5a9054b
vite path normalization and future proofing
ajslater Aug 27, 2026
d20967a
update deps
ajslater Aug 27, 2026
0df671f
v2.2.12
ajslater Aug 27, 2026
17437b9
remove useless news verbosity
ajslater Aug 27, 2026
98c8383
update deps
ajslater Aug 29, 2026
d88f4f1
Report settings counted in registered users
ajslater Aug 30, 2026
e721f15
Rewrap the stats tab's per-user additions
ajslater Aug 30, 2026
23b3a57
update deps
ajslater Aug 30, 2026
9cdb6fa
Sort and read by alternate series (#360) (#830)
ajslater Aug 30, 2026
0d67524
version 2.3.0
ajslater Aug 30, 2026
c571bc9
Remember the last sort for each top collection (#415) (#831)
ajslater Aug 30, 2026
2433bf8
Combine the two unreleased migrations into 0053
ajslater Aug 30, 2026
4dc3bab
Clear the basedpyright errors and warnings
ajslater Aug 30, 2026
afb6278
Merge the Alternate Number sort into the Alternate Series sort (#832)
ajslater Aug 30, 2026
10aaa0f
Fold 0054 into 0053 and rename Alternate Series to Reprints (#833)
ajslater Aug 30, 2026
6671173
update deps
ajslater Sep 2, 2026
2545900
Derive stats tab section titles from the component (#834)
ajslater Sep 2, 2026
a715db5
Adjust for Vuetify 4.2.0 (#835)
ajslater Sep 2, 2026
5c917f5
update deps
ajslater Sep 2, 2026
16eeb72
Scroll to the right page when reading Bottom to Top (#836)
ajslater Sep 2, 2026
85f47ff
update deps
ajslater Sep 8, 2026
79122e7
Comicbox 5 (#839)
ajslater Sep 9, 2026
36f9794
update deps
ajslater Sep 9, 2026
72af4b2
Clear basedpyright, ty, and radon complexity warnings
ajslater Sep 9, 2026
c5a2254
Auto Update on by default outside Docker, honest per-source tagging s…
ajslater Sep 9, 2026
b0a92e1
Make the stored-id prepass report what it did (#841)
ajslater Sep 9, 2026
419126b
update deps
ajslater Sep 9, 2026
6dce467
Merge branch 'main' into develop
ajslater Sep 9, 2026
5184e0d
update deps
ajslater Sep 10, 2026
cb0fdd4
Fix `make docs`: build the site outside docs_dir (#843)
ajslater Sep 12, 2026
c7cb4bd
Republish a deprecated Docker Hub mirror and warn its admins to move …
ajslater Sep 12, 2026
4d590ec
update version to 2.3.1
ajslater Sep 12, 2026
9e3c9ca
Let the color prop work on snackbars (#845)
ajslater Sep 12, 2026
14df7e8
Warn the logs, not just the web UI, about the deprecated Docker Hub i…
ajslater Sep 12, 2026
33b14b7
bump news
ajslater Sep 12, 2026
b1ad810
news bump
ajslater Sep 12, 2026
edaa437
Merge branch 'main' into develop
ajslater Sep 12, 2026
2916299
update devenv deps
ajslater Sep 13, 2026
d0ebdef
update deps
ajslater Sep 13, 2026
590e3e8
run fix
ajslater Sep 13, 2026
43f7d2f
fix typecheck & complexity warnings
ajslater Sep 13, 2026
11b3210
Fix nightly janitor running dozens of times each midnight (#848)
ajslater Sep 13, 2026
1b7c8db
make news more terse
ajslater Sep 13, 2026
7402dfc
bump version
ajslater Sep 13, 2026
f92bbf0
Fix the ASGI pool concurrency test racing on WAL conversion (#850)
ajslater Sep 13, 2026
ba7484c
Merge branch 'main' into develop
ajslater Sep 14, 2026
1ece089
Merge branch 'develop' of github.com:ajslater/codex into develop
ajslater Sep 14, 2026
84d6329
update deps
ajslater Sep 17, 2026
c204ebe
Adapt to comicbox 5.1 and @vueuse/core 15 (#851)
ajslater Sep 17, 2026
b53a7e4
Apply an online-tagging match prompt to every issue of its series (#852)
ajslater Sep 17, 2026
0f466a4
Merge branch 'main' into develop
ajslater Sep 17, 2026
a5d45fc
update deps
ajslater Sep 18, 2026
08ffb02
Fix mark-read confirm gate never firing from a browser card (#857)
ajslater Sep 19, 2026
e5f06ff
update deps
ajslater Sep 20, 2026
552a321
dont mount comics readonly in the compose example for docker
ajslater Sep 20, 2026
0790a72
fix(importer): an unreadable directory is not a deleted one (#858)
ajslater Sep 20, 2026
de09c59
Restore OPDS library browsing for every client (#855) (#859)
ajslater Sep 20, 2026
d8804aa
fix(importer): look twice before deleting a comic (#860)
ajslater Sep 20, 2026
bdccb5c
fix(poller): pair a move that lost its inode, and stop truncating mti…
ajslater Sep 20, 2026
3783a84
fix(janitor): stop pruning folders that hold uppercase comics (#861)
ajslater Sep 20, 2026
bbd1118
fix(tagging): answer the four things #854 reported (#863)
ajslater Sep 20, 2026
4c67da4
fix(janitor): make the orphan-bookmark sweep able to run (#864)
ajslater Sep 20, 2026
e646ef0
update deps
ajslater Sep 20, 2026
d145654
feat(tagging): link a refused conversion to the comic it became (#865)
ajslater Sep 20, 2026
ec8ca32
fix(bookmarks): one bookmark per owner per comic (#866)
ajslater Sep 20, 2026
b8f3fcf
fix(auth): enforce the register-mode password minimum (#867)
ajslater Sep 20, 2026
cc7a92f
refactor(auth): remove the auth-form mixin's never-run setup() (#868)
ajslater Sep 21, 2026
74761f7
fix(metadata): gate edit saves on field validation (#869)
ajslater Sep 21, 2026
4d5f89f
Install Vuetify's rules plugin and convert the validation call sites …
ajslater Sep 21, 2026
2af1834
Theme consolidation: WS1–WS9 (#871)
ajslater Sep 21, 2026
813be2b
depend on comicbox 5.2.0
ajslater Sep 21, 2026
243b54d
feat(tagging): enlarge a match candidate's cover on hover (#872)
ajslater Sep 21, 2026
86ae49f
comicbox 5.2.0 backend adoption (#873)
ajslater Sep 21, 2026
85db3ad
refactor(covers): move both legacy cover popups onto the shared compo…
ajslater Sep 21, 2026
3fc61ff
Cascade layers, app-wide (#875)
ajslater Sep 21, 2026
9c377bf
fix constant name in claude.md
ajslater Sep 21, 2026
2a773c0
fix(admin): two live client validation bugs (#876)
ajslater Sep 21, 2026
b910070
feat(models): add the missing_since column (#878)
ajslater Sep 21, 2026
0adda84
chore(theme): declare the on-primary token (#877)
ajslater Sep 21, 2026
3663908
feat(choices): a shared limits transport (#885)
ajslater Sep 21, 2026
4c884a9
fix(admin): reject values that break the app (#886)
ajslater Sep 21, 2026
a1982f8
fix(admin): type the from address, bound the lengths (#887)
ajslater Sep 21, 2026
75ad92e
fix(admin): show the server's reason on the field that caused it (#888)
ajslater Sep 21, 2026
a1a493d
feat(views): hide scanner-stamped rows from ordinary users (#879)
ajslater Sep 21, 2026
fffa588
feat(scribe): keep a vanished row instead of deleting it (#880)
ajslater Sep 21, 2026
1fa4a54
feat(poller): revive a row whose file came back (#881)
ajslater Sep 21, 2026
83ab1c0
feat(janitor): reap expired pending deletes nightly (#882)
ajslater Sep 21, 2026
ed1c908
feat(admin): a Pending Deletes panel in the Libraries tab (#883)
ajslater Sep 21, 2026
396464d
fix(janitor): custom cover cleanup fails closed on an unreadable path…
ajslater Sep 21, 2026
47b609d
fix(admin): a cleared poll-interval part no longer emits "null" (#889)
ajslater Sep 21, 2026
37128ae
update deps
ajslater Sep 21, 2026
af272c1
fix(tests): give each run its own fixture directories (#892)
ajslater Sep 21, 2026
912e52f
fix(importer): scope every path lookup to its own library (#893)
ajslater Sep 21, 2026
5d887b4
fix(poller): carry Force Update as a diff flag (#894)
ajslater Sep 21, 2026
64f197d
fix(admin): library delete and membership changes broadcast again (#899)
ajslater Sep 21, 2026
5f03625
fix(browser): container names use the full ACL, not just the missing …
ajslater Sep 21, 2026
63ff6a5
fix(onlinetag): release Comic Vine connections with Metron's (#903)
ajslater Sep 21, 2026
4f5d2bf
fix(views): ACL the raw pks that reach the librarian queues (#896)
ajslater Sep 21, 2026
8e68afa
fix(views): a comic file that cannot be read is Not Found (#897)
ajslater Sep 21, 2026
f2133e8
fix(browser): collection rows summarize only visible comics (#901)
ajslater Sep 21, 2026
99270b6
fix(opds): one unreadable comic no longer empties a v1 feed (#898)
ajslater Sep 21, 2026
bbd2536
fix(covers): derive the cover namespace from one custom flag (#900)
ajslater Sep 21, 2026
e3b703e
fix(browser): an unresolvable collection redirects up a level (#902)
ajslater Sep 21, 2026
cdfff8b
fix import in breadcrumbs
ajslater Sep 21, 2026
c37b041
fix(librarian): the importer's FTS statuses are actually finished
ajslater Sep 22, 2026
90a4533
refactor(reader): extract the settings comic prefetch
ajslater Sep 22, 2026
1c5730e
refactor(tests): split the two rank-C test blocks
ajslater Sep 22, 2026
e2dd2d6
fix(tests): breadcrumbs fixtures go back to per-run directories
ajslater Sep 22, 2026
e176e56
docs(news): condense the v2.3.4 fixes
ajslater Sep 22, 2026
a124914
fix(tests): the last six fixture dirs go per-run (#904)
ajslater Sep 22, 2026
a8e36b0
fix(browser): collection row aggregates were summing distinct values …
ajslater Sep 22, 2026
76a5add
feat(browser): show read state under every cover (#906)
ajslater Sep 22, 2026
0940587
docs(news): read state and collection counting for v2.3.4 (#908)
ajslater Sep 22, 2026
c2baca4
Fold the post-v2.3.3 migrations into one (#909)
ajslater Sep 22, 2026
7b85655
chore(settings): pin five options on and take their toggles away (#910)
ajslater Sep 22, 2026
71f8f1e
fix(browser): hide pending-delete rows from admins too (#911)
ajslater Sep 22, 2026
f9aea1b
fix(scribe): the watcher hard-deleted, so retention never happened (#…
ajslater Sep 22, 2026
03cc3a8
update version to v2.4.0 and add feature titles
ajslater Sep 22, 2026
e152993
chore(settings): remove show_read_state as if it never existed
ajslater Sep 22, 2026
d7355b6
Merge branch 'main' into develop
ajslater Sep 22, 2026
3991389
fix(browser): restore scrolling — Vuetify ships one unlayered stylesh…
ajslater Sep 22, 2026
0427c7e
bump version and news. 2.4.1
ajslater Sep 22, 2026
abc9833
news language
ajslater Sep 22, 2026
a68f10b
test(rename-toggle): cancel the real debounce timer mountEditPanel le…
ajslater Sep 22, 2026
0a9d642
Merge branch 'main' into develop
ajslater Sep 22, 2026
db89f5d
fix(opds): position endpoint 404s for session-less clients (#917)
ajslater Sep 23, 2026
1a89299
Merge branch 'main' into develop
ajslater Sep 23, 2026
dc62db5
Merge branch 'main' into develop
ajslater Sep 23, 2026
9d33d74
fix(opds): no-position GET is 200 + empty payload; Keep Reading lists…
ajslater Sep 24, 2026
0dd611f
feat(opds): position PUT returns the stored document; errors are Prob…
ajslater Sep 24, 2026
a421fbe
fix(bookmarks): UNREAD is one identity-scoped NOT EXISTS (#921)
ajslater Sep 24, 2026
0365a03
build(frontend): drop the Vuetify unlayered-CSS plugin; 4.2.2 fixes i…
ajslater Sep 24, 2026
4f0c9bd
bump news
ajslater Sep 24, 2026
916196a
Merge branch 'main' into develop
ajslater Sep 24, 2026
7888061
update deps
ajslater Sep 27, 2026
6570b98
feat(onlinetag): show the file's own cover in match review (#927)
ajslater Sep 27, 2026
2a74752
ci: release job (tag, GitHub Release, merge-back) + Release Preflight…
ajslater Sep 27, 2026
015e8dc
ci: split "Lint, Test & Build Dist" into gate, fail-fast check matrix…
ajslater Sep 27, 2026
5c195fd
feat(admin): Defaults tab for site-wide default browser and reader se…
ajslater Sep 27, 2026
46c189f
update deps
ajslater Sep 27, 2026
a1d604d
organize news better
ajslater Sep 27, 2026
2b52129
fix(admin): explain the locked Show Collections box on the Defaults t…
ajslater Sep 27, 2026
266027e
refactor(onlinetag): move the file's cover into the prompt's panel ti…
ajslater Sep 27, 2026
eb88907
fix(onlinetag): let the title cover enlarge; tint the file under revi…
ajslater Sep 27, 2026
69454e9
fix(lint): make the ESLint base presets actually apply (#931)
ajslater Sep 28, 2026
63f0600
update deps
ajslater Sep 28, 2026
51f1567
refactor(frontend): fix the findings parked by the ESLint preset fix …
ajslater Sep 28, 2026
30e3468
Merge branch 'develop' of github.com:ajslater/codex into develop
ajslater Sep 28, 2026
e368049
fix(admin): keep the Add/Edit dialog open when the server rejects a s…
ajslater Sep 28, 2026
0e5d1fd
fix(reader): stop the one-page step from moving past the book's ends …
ajslater Sep 28, 2026
2e67818
fix(reader): write each debounced bookmark to the book it was read in…
ajslater Sep 28, 2026
5c96f59
fix(reader): land settings responses only on the book they were for (…
ajslater Sep 28, 2026
2dd8a9b
fix(reader): resolve loadGlobalSettings after the settings land (#937)
ajslater Sep 28, 2026
dbdfe8d
fix(browser): send only the filters in use with bulk and metadata req…
ajslater Sep 28, 2026
1c7d069
fix(admin): Add/Edit submit button follows the newest edit (#939)
ajslater Sep 28, 2026
2240f36
fix(auth): let kiosk sessions save their timezone again (#940)
ajslater Sep 28, 2026
056e688
test(admin): cover the table engine (#947)
ajslater Sep 28, 2026
04d5d36
test(browser): characterize top-collection validation (#941)
ajslater Sep 28, 2026
d32a7c2
chore(browser): delete dead browser store state and actions (#942)
ajslater Sep 28, 2026
ad3b526
fix(reader): drop dead store bindings; make the "b" shortcut set bott…
ajslater Sep 28, 2026
a2b2389
fix(reader): stop a redundant settings request and a stale zoom fit (…
ajslater Sep 28, 2026
b63f09c
fix(admin): keep the folder picker's root when its path is cleared (#…
ajslater Sep 28, 2026
7275993
perf(frontend): keep the admin store out of the browser and reader bu…
ajslater Sep 28, 2026
faf4c84
refactor(admin): share one sticky-cache test across the admin loads (…
ajslater Sep 28, 2026
f2f4a19
update deps
ajslater Sep 28, 2026
37dec20
perf(frontend): keep the admin API client out of the browser bundle (…
ajslater Sep 28, 2026
fba3862
fix(admin): stop a slow table read from overwriting newer rows (#949)
ajslater Sep 28, 2026
4adced9
fix(reader): name the Original Size fit class fitToOrig (#951)
ajslater Sep 28, 2026
94defbd
fix(reader): fit wide vector PDF pages to the screen (#953)
ajslater Sep 28, 2026
24a6764
fix(admin): stop a slow tag-write errors read from overwriting newer …
ajslater Sep 28, 2026
8c63260
ci: compose CI from devenv's building blocks (#954)
ajslater Sep 29, 2026
5e88c40
update deps
ajslater Sep 29, 2026
4178d14
Merge branch 'develop' of github.com:ajslater/codex into develop
ajslater Sep 29, 2026
3614f27
feat(browser): mark finished items with a check cap, not a thick bar …
ajslater Sep 29, 2026
bf19c90
update deps
ajslater Sep 29, 2026
7f2bb45
docs(news): title v2.5.0 and order its fixes by user interest
ajslater Sep 29, 2026
d7feeeb
update devenv
ajslater Sep 29, 2026
da3bb6a
update deps
ajslater Sep 29, 2026
99b1ff1
bump version to 2.5.0
ajslater Sep 29, 2026
c01bfa5
Merge branch 'main' into develop
github-actions[bot] Sep 29, 2026
722ca0d
fix(browser): an unresolvable collection redirects to the top, not it…
ajslater Sep 29, 2026
7c0f8e3
fix(browser): clearing a search or loading a saved view no longer str…
ajslater Sep 29, 2026
e76ad66
feat(browser): saved views can be deleted (#959)
ajslater Sep 29, 2026
cd292e6
version 2.5.1
ajslater Sep 29, 2026
66b7040
fix(browser): picking a saved view no longer reopens its menu (#960)
ajslater Sep 29, 2026
49927c4
update devenv
ajslater Sep 30, 2026
ea8484e
docs: fix markdown lint errors and readthedocs links
ajslater Sep 30, 2026
d85156c
ci: let the deploy job publish to PyPI with trusted publishing (#961)
ajslater Sep 30, 2026
1554524
update devenv
ajslater Sep 30, 2026
d39b85f
exclude static from codespell
ajslater Sep 30, 2026
74ec962
codespell fixes
ajslater Sep 30, 2026
e71fa6f
fix codespell
ajslater Sep 30, 2026
c010cee
update comicbox
ajslater Sep 30, 2026
dc24718
Adopt comicbox 5.2.2: 404 a reader page past the end (#962)
ajslater Sep 30, 2026
88646dc
Merge branch 'develop' of github.com:ajslater/codex into develop
ajslater Sep 30, 2026
1eb6a7c
update deps
ajslater Sep 30, 2026
db32443
Merge branch 'main' into develop
github-actions[bot] Sep 30, 2026
942f509
chore(docker): drop two dockerignore negations that do nothing (#964)
ajslater Oct 2, 2026
51ba70c
update devenv
ajslater Oct 2, 2026
67f0e7e
Merge branch 'develop' of github.com:ajslater/codex into develop
ajslater Oct 2, 2026
3b43595
perf(docker): uv.lock-pinned venv, ~490 MB smaller image; declare pac…
ajslater Oct 5, 2026
4cc8cec
update devenv
ajslater Oct 5, 2026
83b4b6b
update devenv and deps. fix a slew of unicorn errors
ajslater Oct 5, 2026
a1aa04b
bump version to 2.5.2
ajslater Oct 5, 2026
7ed0b55
Merge branch 'main' into develop
github-actions[bot] Oct 5, 2026
12b215f
update deps
ajslater Oct 6, 2026
c30c36e
Run on (and use) the Python 3.12 floor (#967)
ajslater Oct 6, 2026
aa35ab8
version 2.5.3
ajslater Oct 6, 2026
8330feb
NEWS for v2.5.3
ajslater Oct 6, 2026
7cd2632
update deps
ajslater Oct 7, 2026
8a01e7c
update deps. comicbox 5.3.0
ajslater Oct 7, 2026
c120c28
Adapt to comicbox 5.3.x: doctor tab, probe() credentials, ask match m…
ajslater Oct 7, 2026
3a9c27f
Merge branch 'main' into develop
github-actions[bot] Oct 7, 2026
42836c9
Online tagging: toolbar Review button instead of an auto-opening dial…
ajslater Oct 8, 2026
8cd6ea6
Reader: keys meant for a dialog no longer drive the reader (#971)
ajslater Oct 8, 2026
1b2cd41
fix(make): drop the install recipe whose OpenSSL flags never applied …
ajslater Oct 8, 2026
a136479
move prod-server makefile target from django to codex
ajslater Oct 8, 2026
02605fe
fix indendation
ajslater Oct 8, 2026
47ea14a
update devenv
ajslater Oct 8, 2026
128c3fc
update version
ajslater Oct 8, 2026
1f71dd0
Merge branch 'main' into develop
github-actions[bot] Oct 8, 2026
f9d0382
Read stale pickled tagging-cache entries as misses, not 500s (#974)
ajslater Oct 8, 2026
4bd148d
update version to 2.5.5
ajslater Oct 8, 2026
ed238ed
update devenv
ajslater Oct 8, 2026
a8db51c
update devenv
ajslater Oct 10, 2026
307824e
update devenv
ajslater Oct 10, 2026
3c42f6a
update devenv
ajslater Oct 12, 2026
a74ea21
update python description for 3.15
ajslater Oct 12, 2026
7dc20c1
update deps
ajslater Oct 12, 2026
3a64058
update devenv
ajslater Oct 12, 2026
1083236
update deps
ajslater Oct 15, 2026
7dcf53b
use ububuntu-latest
ajslater Oct 17, 2026
834f24d
update deps and devenv
ajslater Oct 17, 2026
0eb4fc8
lint fixes
ajslater Oct 17, 2026
d28c8f9
first pass for python 3.15
ajslater Oct 18, 2026
df658e5
Install the CI lint tools; update devenv to 0.2.9 (#975)
ajslater Oct 19, 2026
bf6ddc3
update devenv
ajslater Oct 19, 2026
c36f194
update deps
ajslater Oct 19, 2026
bbb0d1f
Clear ty's 99 override errors (#976)
ajslater Oct 19, 2026
dbf7052
Adapt to comicbox 5.3.2 (#977)
ajslater Oct 19, 2026
3463ffb
Add a csrf_trusted_origins setting for reverse proxies (#978)
ajslater Oct 19, 2026
4715894
Note that a correctly configured proxy doesn't need csrf_trusted_orig…
ajslater Oct 19, 2026
0f78256
Remove the four pinned settings from the schema (#981)
ajslater Oct 19, 2026
86080f3
Run the Python suite in parallel and stop vitest's double work (#980)
ajslater Oct 19, 2026
fa3d8c6
remove news item
ajslater Oct 19, 2026
9aec2ce
update devenv
ajslater Oct 19, 2026
26cf6dc
Split the credentials doctor check below radon's C rank
ajslater Oct 19, 2026
82bcd00
add news item
ajslater Oct 19, 2026
4fc33f3
update devenv
ajslater Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devenv-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.0
0.2.11
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
*~
.*cache
.DS_Store
.claude
.claude/settings.local.json
.claude/worktrees
.coverage*
.docker-token
.env*
Expand Down
27 changes: 27 additions & 0 deletions .github/actions/devenv-lint-tools/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Managed by devenv (copy/ci). Edit it there, not in a child repo.
name: devenv lint tools
description: >-
Install the lint tools devenv's bin/ scripts run with Homebrew and put them
on PATH for later steps. The latest of each, as on the dev machines. Works
on ubuntu runners, where Homebrew is installed but not on PATH, and on macOS.
inputs:
tools:
description: Space-separated Homebrew formulae to install.
default: shellcheck shellharden shfmt hadolint dockerfmt actionlint
runs:
using: composite
steps:
- name: Install lint tools with Homebrew
shell: bash
env:
TOOLS: ${{ inputs.tools }}
HOMEBREW_NO_AUTO_UPDATE: "1"
HOMEBREW_NO_INSTALL_CLEANUP: "1"
HOMEBREW_NO_ANALYTICS: "1"
run: |
if [[ -x /home/linuxbrew/.linuxbrew/bin/brew ]]; then
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
fi
read -ra formulae <<<"$TOOLS"
brew install "${formulae[@]}"
echo "$(brew --prefix)/bin" >>"$GITHUB_PATH"
41 changes: 30 additions & 11 deletions .github/workflows/devenv-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
#
# Gate, one CI image build, a fail-fast lint/test/build matrix that pulls that
# image, and the required-check aggregator. With container: false the matrix
# runs natively on inputs.runner instead (for macOS-only projects) and no
# image is built. Call it from a job in ci.yml; later jobs read its outputs
# instead of repeating any trigger logic:
# runs natively on inputs.runner instead (for macOS-only projects), no image
# is built and the lint combo installs the lint tools with brew. Call it from
# a job in ci.yml; later jobs read its outputs instead of repeating any
# trigger logic:
#
# ci:
# uses: ./.github/workflows/devenv-check.yml
Expand Down Expand Up @@ -36,23 +37,24 @@ on:
description: >-
true runs every combo in the CI image built from the Dockerfile.
false runs it natively on the runner after setup-uv, setup-bun and
make install, for projects that cannot run in a Linux container.
make install, for projects that cannot run in a Linux container; a
combo that runs lint installs the lint tools with brew first.
type: boolean
default: true
runner:
description: >-
Runner for the check matrix, e.g. macos-latest with container false.
The gate, image and result jobs stay on ubuntu-24.04.
The gate, image and result jobs stay on ubuntu-latest.
type: string
default: ubuntu-24.04
default: ubuntu-latest
release-preflight:
description: >-
Run bin/release-tag.sh preflight in the gate on main pushes and pull
requests into main.
type: boolean
default: true
retention-days:
description: Days to keep python-dist and the ci-passed marker.
description: Days to keep python-dist, the ci-passed marker and coverage.
type: number
default: 7
outputs:
Expand Down Expand Up @@ -93,7 +95,7 @@ jobs:
# ---------------------------------------------------------------------------
gate:
name: Gate
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
timeout-minutes: 10
if: >-
(github.event_name == 'push' && github.ref_name == 'main')
Expand Down Expand Up @@ -179,7 +181,7 @@ jobs:
name: CI Image
needs: gate
if: inputs.container && needs.gate.outputs.dist_found != 'true'
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
# The slowest job: a cold build without the registry cache.
timeout-minutes: 45
permissions:
Expand Down Expand Up @@ -235,7 +237,7 @@ jobs:
prune:
name: Prune CI Images
needs: image
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
timeout-minutes: 10
continue-on-error: true
permissions:
Expand Down Expand Up @@ -296,6 +298,12 @@ jobs:
if: ${{ !inputs.container }}
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0

# Only the combo that lints pays for the install. In container mode the
# CI stage of the project's Dockerfile has the tools.
- name: Install lint tools
if: ${{ !inputs.container && contains(matrix.make, 'lint') }}
uses: ./.github/actions/devenv-lint-tools

- name: Install
if: ${{ !inputs.container }}
run: |
Expand Down Expand Up @@ -326,6 +334,17 @@ jobs:
name: test-results-${{ strategy.job-index }}
path: "**/test-results/pytest/*.xml"

# pytest-cov's HTML report. A project whose tests write none, such as
# one with pytest addopts of its own, has nothing to upload.
- name: Upload Coverage
if: matrix.junit && !cancelled() && steps.work.outcome != 'skipped'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: coverage-${{ strategy.job-index }}
path: "**/test-results/coverage/"
retention-days: ${{ inputs.retention-days }}
if-no-files-found: ignore

- name: Publish Test Report
if: matrix.junit && !cancelled() && steps.upload-tests.outcome == 'success'
uses: mikepenz/action-junit-report@b7b80d7ac5b1da46d6aaff9fb57ad50d8453bcb3 # v6.6.0
Expand Down Expand Up @@ -354,7 +373,7 @@ jobs:
# always(), not !cancelled(): a skipped required check counts as passing,
# so a cancelled run must still reach this job and go red.
if: ${{ always() && needs.gate.result != 'skipped' }}
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
timeout-minutes: 5
permissions: {}
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/devenv-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ env:
jobs:
release:
name: Tag, Release & Merge to Develop
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write # tag push, develop push, gh release create
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
.DS_Store
.Python
.claude
.claude/settings.local.json
.claude/worktrees/
.coverage*
.coverage.*
.dmypy.json
Expand Down Expand Up @@ -48,7 +50,6 @@ MANIFEST
TODO.md
__pycache__/
__pypackages__/
__snapshots__
build
celerybeat-schedule
celerybeat.pid
Expand Down
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
*.sh
*Dockerfile
.*cache
.claude
.claude/settings.local.json
.claude/worktrees/
.git
.venv*
__pycache__
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
build:
os: ubuntu-24.04
os: ubuntu-lts-latest
tools:
python: "3"
mkdocs:
Expand Down
35 changes: 18 additions & 17 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
###############################################################################

# ---- Stage 1: builder (build tools + Node for compilation) -----------------
FROM nikolaik/python-nodejs:python3.14-nodejs26 AS builder-base
FROM nikolaik/python-nodejs:python3.15-nodejs26 AS builder-base
# nodejs25 blocked on bug https://github.com/nodejs/node/issues/60303

COPY debian.sources /etc/apt/sources.list.d/
Expand All @@ -40,17 +40,18 @@ RUN pip3 install --no-cache --upgrade pip
FROM oven/bun:latest AS bun-source
FROM builder-base AS codex-ci

# hadolint ignore=DL3008
RUN apt-get clean \
&& apt-get update \
&& apt-get install --no-install-recommends -y \
shellcheck \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY --from=bun-source /usr/local/bin/bun /usr/local/bin/bun
COPY --from=bun-source /usr/local/bin/bunx /usr/local/bin/bunx

# Lint tools devenv's bin/ scripts run, unpinned on purpose.
COPY --from=koalaman/shellcheck:stable /bin/shellcheck /usr/local/bin/
COPY --from=mvdan/shfmt:latest /bin/shfmt /usr/local/bin/
COPY --from=hadolint/hadolint:latest /bin/hadolint /usr/local/bin/
COPY --from=rhysd/actionlint:latest /usr/local/bin/actionlint /usr/local/bin/
COPY --from=ghcr.io/reteps/dockerfmt:latest /ko-app/dockerfmt /usr/local/bin/
ADD https://github.com/anordal/shellharden/releases/latest/download/shellharden-x86_64-unknown-linux-musl.tar.gz /tmp/shellharden.tar.gz
RUN tar -xzf /tmp/shellharden.tar.gz -C /usr/local/bin shellharden && rm /tmp/shellharden.tar.gz

WORKDIR /app

# Python deps (cacheable when lockfiles unchanged)
Expand Down Expand Up @@ -86,7 +87,7 @@ COPY pyproject.toml uv.lock ./
COPY dist/${CODEX_WHEEL} ./
# Pin the runtime closure to uv.lock; install the wheel itself without deps.
RUN uv export --frozen --no-default-groups --no-emit-project -o requirements.txt \
&& uv venv --python /usr/local/bin/python3.14 /opt/codex \
&& uv venv --python /usr/local/bin/python3.15 /opt/codex \
&& PYMUPDF_SETUP_PY_LIMITED_API=0 uv pip install --python /opt/codex/bin/python \
--no-cache --compile-bytecode -r requirements.txt \
&& uv pip install --python /opt/codex/bin/python --no-cache --no-deps \
Expand All @@ -96,15 +97,15 @@ RUN uv export --frozen --no-default-groups --no-emit-project -o requirements.txt
# English: LANGUAGE_CODE is en-us and nothing activates another language.
# hadolint ignore=SC2016
RUN set -eux \
&& find /opt/codex -type f \( -name '*.so' -o -name '*.so.*' \) \
-exec strip --strip-unneeded {} + \
&& find /opt/codex -name '*.pyi' -delete \
&& rm -rf /opt/codex/lib/python3.14/site-packages/pycountry/locales \
&& find /opt/codex/lib/python3.14/site-packages -type d -name locale -prune \
-exec sh -c 'for d in "$1"/*/; do case "$(basename "$d")" in en|__pycache__) ;; *) rm -rf "$d";; esac; done' _ {} \;
&& find /opt/codex -type f \( -name '*.so' -o -name '*.so.*' \) \
-exec strip --strip-unneeded {} + \
&& find /opt/codex -name '*.pyi' -delete \
&& rm -rf /opt/codex/lib/python3.15/site-packages/pycountry/locales \
&& find /opt/codex/lib/python3.15/site-packages -type d -name locale -prune \
-exec sh -c 'for d in "$1"/*/; do case "$(basename "$d")" in en|__pycache__) ;; *) rm -rf "$d";; esac; done' _ {} \;

# ---- Stage 4: final (production image) ------------------------------------
FROM ghcr.io/ajslater/python-debian:3.14.6-slim-trixie_0 AS final
FROM ghcr.io/ajslater/python-debian:3.15.0-slim-trixie_0 AS final
ARG CODEX_VERSION=dev
LABEL org.opencontainers.image.title="Codex" \
org.opencontainers.image.description="Codex Comic Server" \
Expand Down
13 changes: 13 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ width: 128px;
border-radius: 128px;
" />

## v2.5.5

- Features
- `csrf_trusted_origins` setting for CSRF 403s behind a reverse proxy.
- Docker images now use Python 3.15
- Fixes
- Online tagging matches saved for review by 2.5.3 no longer break the
review list and scan status with "No module named 'numpy'". They are
dropped, so those comics need tagging again.
- Renamed comics no longer contain colons or other characters Windows and
SMB shares can't store.
- A Metron API key is no longer overridden by a `~/.netrc` login.

## v2.5.4

- Features
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,11 @@ present.
# websockets = true
# HTTP path prefix for codex (e.g. "/codex" for reverse proxy sub-path)
# url_path_prefix = ""
# Public origins accepted for CSRF when a reverse proxy serves Codex under
# another scheme or host than Codex sees (e.g. https in front of http).
# First make sure the proxy sends X-Forwarded-Proto and X-Forwarded-Host.
# A correctly configured proxy does, and then you shouldn't need this.
# csrf_trusted_origins = ["https://comics.example.com"]

# [logging]
# Log level: TRACE, DEBUG, INFO, SUCCESS, WARNING, ERROR, CRITICAL
Expand Down Expand Up @@ -569,6 +574,9 @@ Environment variables override values set in the TOML config file.
Default true.
- `GRANIAN_URL_PATH_PREFIX` HTTP path prefix for codex (e.g. "/codex" for
reverse proxy sub-path). Defaults to "".
- `CODEX_CSRF_TRUSTED_ORIGINS` Comma-separated public origins (e.g.
`https://comics.example.com`) accepted for CSRF behind a reverse proxy. See
[CSRF 403 behind a proxy](#csrf-403-behind-a-proxy). Defaults to none.

##### Repair

Expand Down Expand Up @@ -697,6 +705,28 @@ of the `/codex` location with `auth_request`, exempt the API the way Codex's own
examples do (`location /codex/api { auth_request off; ... }`); the `^~` note
above is what keeps the static assets reachable.

#### CSRF 403 behind a proxy

If logging in or saving changes fails with a 403 "CSRF Failed" error behind a
reverse proxy, first make sure the proxy sends `X-Forwarded-Proto` and
`X-Forwarded-Host` (the nginx example above does). Codex compares them with the
browser's `Origin` header, so a correctly configured proxy ordinarily needs no
CSRF setting at all. Only if the proxy can't send them, or the error persists,
add the public origin the browser uses to `config/codex.toml`:

```toml
[server]
csrf_trusted_origins = ["https://comics.example.com"]
```

or set `CODEX_CSRF_TRUSTED_ORIGINS="https://comics.example.com"` (comma
separated for more than one). An origin is `scheme://host[:port]` with no path;
`https://*.example.com` trusts every subdomain.

Do not remove Django's `CsrfViewMiddleware` to work around the error. The API
enforces CSRF through Django REST Framework anyway, so removing it doesn't fix
the API, and it strips CSRF protection from Codex's other pages.

#### Preserving the Codex Server Header

Codex identifies itself with a `Server: codex/<version>` response header, which
Expand Down
7 changes: 6 additions & 1 deletion bin/_lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ is_darwin() {
}

# Succeed when the tool is on PATH. Otherwise say so and how to install it, and
# fail, so a caller can skip that tool visibly instead of silently.
# fail, so a caller can skip that tool visibly instead of silently. In CI (CI
# set, as GitHub sets it) a missing tool is an error instead, so a green CI
# run means every tool ran.
need() {
if command -v "$1" >/dev/null 2>&1; then
return 0
fi
if [[ -n ${CI:-} ]]; then
die "$1 not installed. CI must install it: brew install $1, or add it to the Dockerfile's CI stage"
fi
echo "skipped: $1 not installed (brew install $1)" >&2
return 1
}
Expand Down
Loading
Loading