nice clean recipes pear.dunkirk.sh
recipes
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: userscript install page + static nixos binary build

- Rewrite userscript install page to match pear's casual voice
- Add handleUserscript handler and /userscript route
- Build static musl binary for NixOS (aarch64-linux-musl-gcc)
- Update deploy/rollback to save/restore pear-prev binary
- Remove NixOS preStart go build fallback

💘 Generated with Crush

Assisted-by: Kimi K2.6 via Crush <crush@charm.land>

+148 -10
+11 -8
.github/workflows/deploy.yml
··· 28 28 - name: Install cross-compiler 29 29 run: sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu 30 30 31 + - name: Install musl cross-compiler 32 + run: | 33 + curl -L https://musl.cc/aarch64-linux-musl-cross.tgz | sudo tar xz -C /opt 34 + echo "/opt/aarch64-linux-musl-cross/bin" >> $GITHUB_PATH 35 + 31 36 - name: Build 32 - run: GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc go build -ldflags "-X main.gitHash=$(git rev-parse --short HEAD)" -o pear . 37 + run: GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-musl-gcc go build -ldflags '-linkmode external -extldflags "-static" -X main.gitHash=$(git rev-parse --short HEAD)' -o pear . 33 38 34 39 - name: Setup Tailscale 35 40 uses: tailscale/github-action@v4 ··· 81 86 ssh pear@terebithia << 'INSTALL' 82 87 set -e 83 88 cd ~/app 89 + cp pear pear-prev 84 90 chmod +x pear-new 85 91 mv -f pear-new pear 86 92 sudo /run/current-system/sw/bin/systemctl restart pear.service ··· 132 138 RED="\033[31m" Y="\033[33m" G="\033[32m" D="\033[2m" R="\033[0m" 133 139 cd ~/app 134 140 135 - PREV=$(cat /tmp/pear-prev-commit 2>/dev/null || echo "") 136 - if [ -n "$PREV" ]; then 137 - SHORT=$(echo "$PREV" | cut -c1-7) 138 - echo -e "${Y}:: rolling back to ${SHORT}${R}" 141 + if [ -f pear-prev ]; then 142 + echo -e "${Y}:: rolling back to previous binary${R}" 139 143 sudo /run/current-system/sw/bin/systemctl stop pear.service || true 140 144 141 145 if [ -f /var/lib/pear/data/pear.db.pre-deploy ]; then ··· 143 147 cp /var/lib/pear/data/pear.db.pre-deploy /var/lib/pear/data/pear.db 144 148 fi 145 149 146 - git reset --hard "$PREV" --quiet 147 - CGO_ENABLED=1 go build -ldflags "-X main.gitHash=$(git rev-parse --short HEAD)" -o pear . 150 + mv -f pear-prev pear 148 151 sudo /run/current-system/sw/bin/systemctl restart pear.service 149 - echo -e "${G}:: rolled back to ${SHORT}${R}" 152 + echo -e "${G}:: rolled back to previous binary${R}" 150 153 else 151 154 echo -e "${RED}:: no previous commit recorded, cannot rollback${R}" 152 155 exit 1
+10
main.go
··· 86 86 r.Get("/cook", srv.handleCookView) 87 87 r.Get("/export.cook", srv.handleCookExport) 88 88 r.Get("/recipe", srv.handleRecipeQuery) 89 + r.Get("/userscript", srv.handleUserscript) 89 90 r.Get("/status", srv.handleStatus) 90 91 r.Get("/*", srv.handleRecipePath) 91 92 r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.FS(staticContent)))) ··· 254 255 delete(s.pending, targetURL) 255 256 s.pendingMu.Unlock() 256 257 }() 258 + } 259 + 260 + func (s *Server) handleUserscript(w http.ResponseWriter, r *http.Request) { 261 + data := map[string]interface{}{ 262 + "GitHash": s.gitHash, 263 + "BaseURL": s.baseURL, 264 + } 265 + w.Header().Set("Content-Type", "text/html; charset=utf-8") 266 + s.templates.ExecuteTemplate(w, "userscript_page", data) 257 267 } 258 268 259 269 func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
+69 -1
ui/static/style.css
··· 559 559 color:var(--text-muted); 560 560 font-family:'Poppins',system-ui,sans-serif; 561 561 } 562 - .recent-card:hover .recent-card-name{color:var(--accent)} 562 + .recent-card:hover .recent-card-name{color:var(--accent)} 563 + 564 + .userscript-install{padding:2.5rem 0 1rem;text-align:center} 565 + .userscript-install h2{ 566 + font-family:'Poppins',system-ui,sans-serif; 567 + font-size:2rem; 568 + font-weight:700; 569 + letter-spacing:-0.02em; 570 + margin-bottom:0.5rem; 571 + } 572 + .userscript-desc{color:var(--text-muted);font-size:1.05rem;margin-bottom:2rem} 573 + .install-btn{ 574 + display:inline-block; 575 + padding:0.85rem 2rem; 576 + border-radius:var(--radius); 577 + background:var(--accent); 578 + color:#fff; 579 + border:none; 580 + font-size:1.1rem; 581 + font-family:'Poppins',system-ui,sans-serif; 582 + font-weight:600; 583 + cursor:pointer; 584 + text-decoration:none; 585 + transition:background 0.2s; 586 + margin-bottom:2.5rem; 587 + } 588 + .install-btn:hover{background:var(--accent-hover);text-decoration:none} 589 + .userscript-section{ 590 + text-align:left; 591 + max-width:540px; 592 + margin:0 auto 2rem; 593 + } 594 + .userscript-section h3{ 595 + font-size:0.85rem; 596 + text-transform:uppercase; 597 + letter-spacing:0.08em; 598 + color:var(--text-muted); 599 + border-bottom:2px solid var(--border); 600 + padding-bottom:0.35rem; 601 + margin-bottom:0.75rem; 602 + font-family:'Poppins',system-ui,sans-serif; 603 + } 604 + .userscript-section p{font-size:0.95rem;color:var(--text-muted);line-height:1.6} 605 + .userscript-section ol{margin:0.5rem 0;padding-left:1.5rem;color:var(--text-muted);font-size:0.95rem;line-height:1.8} 606 + .userscript-section li{margin-bottom:0.25rem} 607 + .userscript-managers{ 608 + display:flex; 609 + gap:0.75rem; 610 + margin:0.75rem 0 1rem; 611 + } 612 + .manager-card{ 613 + display:flex; 614 + flex-direction:column; 615 + gap:0.15rem; 616 + padding:0.75rem 1rem; 617 + border:1px solid var(--border); 618 + border-radius:var(--radius); 619 + text-decoration:none; 620 + transition:border-color 0.2s,box-shadow 0.2s; 621 + flex:1; 622 + } 623 + .manager-card:hover{border-color:var(--accent);text-decoration:none;box-shadow:0 2px 8px rgba(0,0,0,0.06)} 624 + .manager-card strong{color:var(--text);font-family:'Poppins',system-ui,sans-serif;font-size:0.9rem} 625 + .manager-card span{color:var(--text-muted);font-size:0.8rem} 626 + 627 + @media(max-width:600px){ 628 + .userscript-install h2{font-size:1.5rem} 629 + .userscript-managers{flex-direction:column} 630 + }
+1 -1
ui/templates/index.html
··· 32 32 <button type="submit">pear it</button> 33 33 </form> 34 34 </div> 35 - <p class="userscript-hint">or <a href="/static/pear.user.js">install the userscript</a> to auto-detect recipes</p> 35 + <p class="userscript-hint">or <a href="/userscript">install the userscript</a> to auto-detect recipes</p> 36 36 </div> 37 37 {{if .Recent}} 38 38 <div class="recent-recipes">
+57
ui/templates/userscript.html
··· 1 + {{define "userscript_page"}} 2 + <!DOCTYPE html> 3 + <html lang="en"> 4 + 5 + <head> 6 + <meta charset="utf-8"> 7 + <meta name="viewport" content="width=device-width,initial-scale=1"> 8 + <title>pear: userscript</title> 9 + <link rel="icon" type="image/svg+xml" href="/static/favicon.svg"> 10 + <link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png"> 11 + <link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png"> 12 + <link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png"> 13 + <link rel="manifest" href="/static/site.webmanifest"> 14 + <link rel="stylesheet" href="/static/style.css"> 15 + </head> 16 + 17 + <body> 18 + <nav> 19 + <a href="/" class="wordmark">pear</a> 20 + </nav> 21 + <div class="page"> 22 + <div class="userscript-install"> 23 + <h2>userscript</h2> 24 + <p class="userscript-desc">auto-detect recipes on any site and provides a link to send you back here.</p> 25 + 26 + <a href="/static/pear.user.js" class="btn-primary install-btn">install userscript</a> 27 + 28 + <div class="userscript-section"> 29 + <h3>how do I install a userscript?</h3> 30 + <div class="userscript-managers"> 31 + <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener" class="manager-card"> 32 + <strong>Tampermonkey</strong> 33 + <span>Chrome, Firefox, Safari, Edge</span> 34 + </a> 35 + <a href="https://violentmonkey.github.io/" target="_blank" rel="noopener" class="manager-card"> 36 + <strong>Violentmonkey</strong> 37 + <span>Chrome, Firefox, Edge</span> 38 + </a> 39 + </div> 40 + <p>install one of these userscript managers, then click the button above.</p> 41 + </div> 42 + 43 + <div class="userscript-section"> 44 + <h3>what it does</h3> 45 + <p>when you're on a recipe site, it checks for recipe data and shows a bar to redirect you to pear.</p> 46 + </div> 47 + </div> 48 + </div> 49 + <footer> 50 + <span>made by <a href="https://dunkirk.sh" target="_blank" rel="noopener">Kieran Klukas</a></span> 51 + <a href="https://tangled.org/dunkirk.sh/pear/commit/{{.GitHash}}" target="_blank" rel="noopener" 52 + class="commit-link">{{.GitHash}}</a> 53 + </footer> 54 + </body> 55 + 56 + </html> 57 + {{end}}