Redirect the Oullin writing archive - #195
Conversation
WalkthroughThe production Caddyfile adds permanent redirects for legacy writing URLs to ChangesWriting archive redirects
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@infra/caddy/caddyfile_prod_test.go`:
- Around line 206-214: Update the archive redirect assertions in the directive
loop to validate each directive’s placement within oullinBlock, not just its
presence. Reuse the existing position-checking approach used elsewhere in the
test and apply it to all three archive redirects, preserving the current
expected order relative to the default proxy.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 27a8f9fb-ba7c-4899-a5e4-e6b043ba9dce
📒 Files selected for processing (2)
infra/caddy/Caddyfile.prodinfra/caddy/caddyfile_prod_test.go
f56eed1 to
d553658
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@infra/caddy/caddyfile_prod_test.go`:
- Around line 220-222: Update the redirect validation in the relevant test to
reject any wildcarded /post source, including /post* and equivalent named
matchers, rather than checking only the literal “redir /post/*” string. Preserve
acceptance of non-broad /post routes while ensuring /post, /post/, and subpaths
cannot be covered by a wildcard matcher.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: dff64cca-3cbd-483f-9bd2-2a8c9671d5de
📒 Files selected for processing (2)
infra/caddy/Caddyfile.prodinfra/caddy/caddyfile_prod_test.go
| if strings.Contains(oullinBlock, "redir /post/*") { | ||
| t.Fatal("must not use a broad /post/* redirect") | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
set -euo pipefail
git ls-files infra/caddy/caddyfile_prod_test.go
echo '---'
ast-grep outline infra/caddy/caddyfile_prod_test.go --view expanded
echo '---'
sed -n '180,250p' infra/caddy/caddyfile_prod_test.goRepository: oullin/api
Length of output: 2793
🏁 Script executed:
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('infra/caddy/caddyfile_prod_test.go')
text = p.read_text()
for needle in ['redir /post/*', 'strings.Contains(oullinBlock', 'named matcher', '/post*']:
print(f'### {needle}')
for i, line in enumerate(text.splitlines(), 1):
if needle in line:
print(i, line)
PYRepository: oullin/api
Length of output: 342
🏁 Script executed:
set -euo pipefail
# Inspect the caddy matcher docs snippet locally in case the repo vendors a reference or comment.
rg -n "redir /post\\*|/post/\\*|matcher" infra/caddy -SRepository: oullin/api
Length of output: 550
🏁 Script executed:
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('infra/caddy/caddyfile_prod_test.go')
for i, line in enumerate(p.read_text().splitlines(), 1):
if 205 <= i <= 235:
print(f"{i:4d}: {line}")
PYRepository: oullin/api
Length of output: 826
🏁 Script executed:
set -euo pipefail
# Inspect the production Caddyfile and any related redirect definitions.
git ls-files infra/caddy
echo '---'
rg -n "redir /post|`@post`|/post\\*" infra/caddy -S
echo '---'
# Show the relevant production block if present.
python3 - <<'PY'
from pathlib import Path
for path in [Path('infra/caddy/Caddyfile'), Path('infra/caddy/caddyfile_prod_test.go')]:
if path.exists():
print(f'## {path}')
for i, line in enumerate(path.read_text().splitlines(), 1):
if 1 <= i <= 260 and ('/post' in line or 'redir' in line or '`@post`' in line or 'reverse_proxy' in line):
print(f"{i:4d}: {line}")
PYRepository: oullin/api
Length of output: 8925
🌐 Web query:
Caddy path matcher /post* /post/* matcher docs redir shortcut directive
💡 Result:
In Caddy, path matching is exact by default [1][2]. To match a path prefix, you must append an asterisk () [1][3][2]. Understanding the difference between /post and /post/* is critical [1][3]: - /post* matches /post, /post/, /postbar, and /post/bar [1][3]. - /post/* matches /post/ and /post/bar, but does NOT match /post or /postbar [1][3]. For most use cases where you want to match a path and all its sub-paths (e.g., to redirect a directory), /post/* is generally the preferred and more predictable choice [1][3][4]. The redir directive is used to issue HTTP redirects [5][6]. It accepts an optional matcher and the target location [5][7]. Common patterns: To redirect a specific path and all its sub-paths to a new location while preserving the remaining URI: redir /post/* https://example.com/new-post{uri} To redirect everything: redir https://example.com{uri} If you are using handle_path, note that it implicitly strips the matched path prefix, which can simplify your configuration if you need to perform actions on the remaining path segment after the prefix [8]. Caddyfile directives are evaluated based on a specific order, and redir is placed very early in the request pipeline [5][6][9], meaning it will trigger before most other directives (like reverse_proxy) if it matches the request [5].
Citations:
- 1: https://caddyserver.com/docs/caddyfile/matchers
- 2: Release notes for 2.0 beta 13 [final] caddyserver/caddy#2981
- 3: https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/matchers.go
- 4: https://caddyexamples.com/examples/routing-multiple-paths-to-a-reverse-proxy/
- 5: https://caddyserver.com/docs/caddyfile/directives/redir
- 6: https://github.com/caddyserver/website/blob/master/src/docs/markdown/caddyfile/directives/redir.md
- 7: https://caddy.guide/docs/caddyfile/directives/redir
- 8: https://caddyserver.com/docs/caddyfile/directives/handle_path
- 9: https://caddyserver.com/docs/caddyfile/directives
🏁 Script executed:
set -euo pipefail
# Check whether the repository includes tests or docs that spell out the intended matcher shape.
rg -n "must not use a broad /post|broad /post|writing redirect|matcher" . -SRepository: oullin/api
Length of output: 959
Reject broad /post matchers, not just redir /post/*. This check still misses redir /post* and equivalent named matchers, which would cover /post, /post/, and subpaths. Assert against wildcarded /post sources, not just the literal /post/*.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infra/caddy/caddyfile_prod_test.go` around lines 220 - 222, Update the
redirect validation in the relevant test to reject any wildcarded /post source,
including /post* and equivalent named matchers, rather than checking only the
literal “redir /post/*” string. Preserve acceptance of non-broad /post routes
while ensuring /post, /post/, and subpaths cannot be covered by a wildcard
matcher.
Source: MCP tools
What changed
.mdpath while sending it to the clean Gocanto route/writing,/writing/, and/tags/*to the Gocanto Writing index/post/*ruleRollout
The complete Gocanto archive is already live and every destination HTML and raw Markdown route has been verified with status 200. Merging this pull request activates the public cutover through the existing VPS deployment workflow.
Validation
go test ./infra/caddymake caddy-validatewith generated scratch mTLS certificatesSummary by CodeRabbit
/writing,/writing/, and/tags/*to the writing archive.