Compare commits

...

6 Commits

Author SHA1 Message Date
will d214420046 Write authors page introducing Cate Conners and Michael Ferrara
deploy / deploy (push) Successful in 6s
Contrasts the two pen names' voices and links out to each author's
own site, giving the press a real "who's behind this" page.
2026-08-11 10:52:00 -04:00
will 3b20b91cb3 Publish a stable, non-fingerprinted stylesheet copy for bookshop
deploy / deploy (push) Successful in 6s
Mirrors thistleandmoth-site's baseof.html: the fingerprinted stylesheet
link changes hash on every deploy, so it can't be hardcoded as
BRAND_SITE_STYLESHEET_URL for bookshop's pages to link. Publishes the same
source at a stable /css/main.min.css URL alongside it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 17:59:53 -04:00
will 6018a1f32d Switch deploy to CI-build + scoped rsync instead of shared-runner bind-mount
deploy / deploy (push) Successful in 5s
Fleet-wide change: CI now builds the site in an isolated container and
pushes output via rsync to a forced-command SSH key restricted (via
rrsync) to this site's own docroot only, instead of relying on the
shared Actions runner's blanket /var/www/** bind-mount capability. See
the static-site-deploy Ansible role in reground-infrastructure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 12:30:23 -04:00
will 9dd53fdb96 Collect first/last name on the mailing-list subscribe form
deploy / deploy (push) Successful in 4s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 19:47:32 -04:00
will 911dacb1b2 Increase header logo size from 44px to 64px
deploy / deploy (push) Successful in 4s
Feedback that the banner logo was too small; the display size is
set entirely by CSS (source image and Hugo's resize pipeline both
have plenty of resolution headroom above 64px).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 17:45:23 -04:00
will 4d0b976c18 Add a submissions on/off toggle with a mailing-list signup fallback
deploy / deploy (push) Successful in 4s
accepting_submissions: false in the submissions page frontmatter now
swaps the manuscript form for a closed notice and an email signup that
posts to the eec service's new /subscribe endpoint, so visitors can
still join the mailing list while submissions are paused.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 20:16:15 -04:00
6 changed files with 102 additions and 7 deletions
+21 -3
View File
@@ -7,10 +7,28 @@ jobs:
runs-on: ubuntu-latest
container:
image: hugomods/hugo:exts
volumes:
- /var/www/twosquirrelspress.com:/var/www/twosquirrelspress.com
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- run: hugo --minify -d /var/www/twosquirrelspress.com
- name: Install rsync + ssh client
run: |
if command -v apk >/dev/null; then
apk add --no-cache rsync openssh-client
else
apt-get update && apt-get install -y rsync openssh-client
fi
- run: hugo --minify -d public
- name: Deploy via rsync
run: |
mkdir -p ~/.ssh
printf '%s\n' "$DEPLOY_SSH_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
# The server-side key is forced-command, restricted via rrsync to
# this site's own docroot only (see the static-site-deploy
# Ansible role) — it can push new files but can never read/list
# anything back off the server, and can't reach any other site's
# directory regardless of what path is requested here.
rsync -az --delete -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new" public/ root@twosquirrelspress.com:
env:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
+1 -1
View File
@@ -94,7 +94,7 @@ header.site .brand {
text-decoration: none;
}
header.site .brand img {
height: 44px;
height: 64px;
width: auto;
display: block;
}
+7 -1
View File
@@ -3,4 +3,10 @@ title: "Authors"
description: "Authors published by Two Squirrels Press."
---
Coming soon.
Two Squirrels Press is run by two people who write very different books.
**Cate Conners** writes Fantasy Michigan — old-growth forests, lakes that keep their own counsel, the kind of magic that's been waiting under the water a long time before anyone noticed. [Read her work →](https://cateconners.com)
**Michael Ferrara** writes short fiction about the ordinary things that turn out to be enormous — AIs, wisecracking seagulls, songs that need to be heard. [Read his work →](https://michaelferrara.org)
Different genres, different voices, same instinct: character first, and trust the reader to meet you halfway.
+1
View File
@@ -1,6 +1,7 @@
---
title: "Submissions"
description: "How to submit work to Two Squirrels Press."
accepting_submissions: false
---
We're always glad to look at character-driven speculative fiction and poetry. Markdown is our preferred format, but we'll happily take a `.docx`, `.odt`, or most other common document formats.
+12 -2
View File
@@ -12,8 +12,18 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&family=Lato:wght@400;600;700&family=Dancing+Script&display=swap">
{{ $css := resources.Get "css/main.css" }}
{{ if hugo.IsProduction }}
{{ $css = $css | minify | fingerprint }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}" integrity="{{ $css.Data.Integrity }}">
{{ $cssFingerprinted := $css | minify | fingerprint }}
<link rel="stylesheet" href="{{ $cssFingerprinted.RelPermalink }}" integrity="{{ $cssFingerprinted.Data.Integrity }}">
{{/* Also publish the same source at a second, stable, non-fingerprinted
URL — the fingerprinted one above changes hash on every deploy, so
it's unusable as a link target for an external same-origin
consumer (bookshop's pages, via BRAND_SITE_STYLESHEET_URL) that
can't track that hash. Referencing .RelPermalink is what makes
Hugo actually publish this resource; nothing else in this
template links it. Stray non-whitespace text directly in <head>
gets moved into <body> by the HTML parser, so this has to sit
inside a comment. */}}
<!-- stable stylesheet copy: {{ ($css | minify).RelPermalink }} -->
{{ else }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}">
{{ end }}
+60
View File
@@ -2,6 +2,7 @@
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ if ne .Params.accepting_submissions false }}
<form id="submissions-form" style="margin-top:2rem;">
<label for="name">Name</label>
<input type="text" id="name" name="name" required autocomplete="name">
@@ -65,4 +66,63 @@
});
})();
</script>
{{ else }}
<p style="margin-top:2rem;">We're not currently accepting submissions, but you can join our mailing list to hear when that changes.</p>
<form id="subscribe-form" style="margin-top:1.5rem;">
<label for="subscribe-first-name">First name</label>
<input type="text" id="subscribe-first-name" name="first_name" required autocomplete="given-name">
<label for="subscribe-last-name">Last name (optional)</label>
<input type="text" id="subscribe-last-name" name="last_name" autocomplete="family-name">
<label for="email">Email</label>
<input type="email" id="email" name="email" required autocomplete="email">
<div class="field-honeypot" aria-hidden="true">
<label for="website">Leave this field empty</label>
<input type="text" id="website" name="website" tabindex="-1" autocomplete="off">
</div>
<button type="submit" class="btn">Join the mailing list</button>
</form>
<p id="subscribe-status" role="status" style="margin-top:1rem;"></p>
<script>
(function () {
var form = document.getElementById("subscribe-form");
var status = document.getElementById("subscribe-status");
form.addEventListener("submit", function (e) {
e.preventDefault();
var payload = {
email: form.email.value,
first_name: form.first_name.value,
last_name: form.last_name.value,
website: form.website.value
};
var submitBtn = form.querySelector("button[type=submit]");
submitBtn.disabled = true;
status.textContent = "Signing you up…";
fetch("{{ .Site.Params.eec_base_url }}/subscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
}).then(function (res) {
submitBtn.disabled = false;
if (res.ok) {
form.reset();
status.textContent = "Thanks — we'll let you know.";
return;
}
return res.text().then(function (msg) {
status.textContent = msg || "Something went wrong signing you up. Try again, or email hello@twosquirrelspress.com directly.";
});
}).catch(function () {
submitBtn.disabled = false;
status.textContent = "Something went wrong signing you up. Try again, or email hello@twosquirrelspress.com directly.";
});
});
})();
</script>
{{ end }}
{{ end }}