name: Build & Deploy on: push: branches: [main] workflow_dispatch: schedule: - cron: "0 * * * *" jobs: build-and-deploy: runs-on: freebsd steps: - uses: actions/checkout@v4 - name: Set up SSH run: | mkdir -p ~/.ssh printf '%s\n' "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -p 222 ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 2>/dev/null - name: Restore node_modules cache id: nm-cache run: | CACHE_KEY=$(md5 -q package-lock.json) CACHE_DIR=/usr/local/git/.cache/node_modules/${CACHE_KEY} if [ -d "$CACHE_DIR" ]; then echo "Restoring node_modules (${CACHE_KEY})" rsync -a --exclude='.bin/' "$CACHE_DIR/" node_modules/ npm rebuild --silent echo "hit=true" >> $GITHUB_OUTPUT else echo "hit=false" >> $GITHUB_OUTPUT echo "CACHE_KEY=$CACHE_KEY" >> $GITHUB_ENV fi - name: Install dependencies if: steps.nm-cache.outputs.hit != 'true' run: npm ci - name: Save node_modules cache if: steps.nm-cache.outputs.hit != 'true' run: | mkdir -p /usr/local/git/.cache/node_modules/${CACHE_KEY} rsync -a --delete --exclude='.bin/' node_modules/ /usr/local/git/.cache/node_modules/${CACHE_KEY}/ - name: Build or restore sharp for FreeBSD run: | SHARP_VER=$(node -e "process.stdout.write(require('./node_modules/sharp/package.json').version)") CACHE_FILE=/usr/local/git/.cache/sharp-freebsd/sharp-freebsd-x64-${SHARP_VER}.node if [ -f "$CACHE_FILE" ]; then echo "Restoring cached sharp ${SHARP_VER}" mkdir -p node_modules/sharp/src/build/Release cp "$CACHE_FILE" node_modules/sharp/src/build/Release/sharp-freebsd-x64.node else echo "Building sharp ${SHARP_VER} from source..." npm install node-addon-api node-gyp npm install sharp --build-from-source mkdir -p /usr/local/git/.cache/sharp-freebsd BUILT=$(find node_modules/sharp -name "sharp-freebsd-x64.node" | head -1) if [ -n "$BUILT" ]; then cp "$BUILT" "$CACHE_FILE" echo "Cached sharp binary at $CACHE_FILE" fi fi - name: Restore eleventy-fetch cache run: | if [ -d /usr/local/git/.cache/eleventy-fetch ]; then echo "Restoring eleventy-fetch cache" mkdir -p .cache rsync -a /usr/local/git/.cache/eleventy-fetch/ .cache/ fi - name: Fetch homepage config from node jail run: | mkdir -p content/.indiekit ssh -p 222 \ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \ "doas bastille cmd node cat /usr/local/indiekit/content/.indiekit/homepage.json" \ > content/.indiekit/homepage.json - name: Build CSS run: npm run build:css - name: Create .env file env: SITE_URL: ${{ secrets.SITE_URL }} SITE_NAME: ${{ secrets.SITE_NAME }} SITE_SOCIAL: ${{ secrets.SITE_SOCIAL }} AUTHOR_NAME: ${{ secrets.AUTHOR_NAME }} SITE_DESCRIPTION: ${{ secrets.SITE_DESCRIPTION }} AUTHOR_BIO: ${{ secrets.AUTHOR_BIO }} AUTHOR_EMAIL: ${{ secrets.AUTHOR_EMAIL }} AUTHOR_LOCATION: ${{ secrets.AUTHOR_LOCATION }} GITHUB_USERNAME: ${{ secrets.GH_USERNAME }} MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} MASTODON_USER: ${{ secrets.MASTODON_USER }} BLUESKY_HANDLE: ${{ secrets.BLUESKY_HANDLE }} ACTIVITYPUB_HANDLE: ${{ secrets.ACTIVITYPUB_HANDLE }} AUTHOR_AVATAR: ${{ secrets.AUTHOR_AVATAR }} AUTHOR_TITLE: ${{ secrets.AUTHOR_TITLE }} AUTHOR_PRONOUN: ${{ secrets.AUTHOR_PRONOUN }} SITE_LOCALE: ${{ secrets.SITE_LOCALE }} OWNYOURSWARM_FEED_URL: ${{ secrets.OWNYOURSWARM_FEED_URL }} OWNYOURSWARM_FEED_TOKEN: ${{ secrets.OWNYOURSWARM_FEED_TOKEN }} LISTENING_FETCH_CACHE_DURATION: ${{ secrets.LISTENING_FETCH_CACHE_DURATION }} FUNKWHALE_FETCH_CACHE_DURATION: ${{ secrets.FUNKWHALE_FETCH_CACHE_DURATION }} LASTFM_FETCH_CACHE_DURATION: ${{ secrets.LASTFM_FETCH_CACHE_DURATION }} run: | { printf 'SITE_URL=%s\n' "$SITE_URL" printf 'SITE_NAME=%s\n' "$SITE_NAME" printf 'SITE_SOCIAL=%s\n' "$SITE_SOCIAL" printf 'AUTHOR_NAME=%s\n' "$AUTHOR_NAME" printf 'SITE_DESCRIPTION=%s\n' "$SITE_DESCRIPTION" printf 'AUTHOR_BIO=%s\n' "$AUTHOR_BIO" printf 'AUTHOR_EMAIL=%s\n' "$AUTHOR_EMAIL" printf 'AUTHOR_LOCATION=%s\n' "$AUTHOR_LOCATION" printf 'GITHUB_USERNAME=%s\n' "$GITHUB_USERNAME" printf 'MASTODON_INSTANCE=%s\n' "$MASTODON_INSTANCE" printf 'MASTODON_USER=%s\n' "$MASTODON_USER" printf 'BLUESKY_HANDLE=%s\n' "$BLUESKY_HANDLE" printf 'ACTIVITYPUB_HANDLE=%s\n' "$ACTIVITYPUB_HANDLE" printf 'AUTHOR_AVATAR=%s\n' "$AUTHOR_AVATAR" printf 'AUTHOR_TITLE=%s\n' "$AUTHOR_TITLE" printf 'AUTHOR_PRONOUN=%s\n' "$AUTHOR_PRONOUN" printf 'SITE_LOCALE=%s\n' "$SITE_LOCALE" printf 'OWNYOURSWARM_FEED_URL=%s\n' "$OWNYOURSWARM_FEED_URL" printf 'OWNYOURSWARM_FEED_TOKEN=%s\n' "$OWNYOURSWARM_FEED_TOKEN" printf 'LISTENING_FETCH_CACHE_DURATION=%s\n' "$LISTENING_FETCH_CACHE_DURATION" printf 'FUNKWHALE_FETCH_CACHE_DURATION=%s\n' "$FUNKWHALE_FETCH_CACHE_DURATION" printf 'LASTFM_FETCH_CACHE_DURATION=%s\n' "$LASTFM_FETCH_CACHE_DURATION" } > .env - name: Build site run: npm run build env: SITE_URL: ${{ secrets.SITE_URL }} SITE_NAME: ${{ secrets.SITE_NAME }} SITE_SOCIAL: ${{ secrets.SITE_SOCIAL }} SITE_DESCRIPTION: ${{ secrets.SITE_DESCRIPTION }} AUTHOR_NAME: ${{ secrets.AUTHOR_NAME }} ACTIVITYPUB_HANDLE: ${{ secrets.ACTIVITYPUB_HANDLE }} AUTHOR_AVATAR: ${{ secrets.AUTHOR_AVATAR }} AUTHOR_BIO: ${{ secrets.AUTHOR_BIO }} AUTHOR_EMAIL: ${{ secrets.AUTHOR_EMAIL }} GITHUB_USERNAME: ${{ secrets.GH_USERNAME }} MASTODON_INSTANCE: ${{ secrets.MASTODON_INSTANCE }} MASTODON_USER: ${{ secrets.MASTODON_USER }} BLUESKY_HANDLE: ${{ secrets.BLUESKY_HANDLE }} GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} OWNYOURSWARM_FEED_URL: ${{ secrets.OWNYOURSWARM_FEED_URL }} OWNYOURSWARM_FEED_TOKEN: ${{ secrets.OWNYOURSWARM_FEED_TOKEN }} LISTENING_FETCH_CACHE_DURATION: ${{ secrets.LISTENING_FETCH_CACHE_DURATION }} FUNKWHALE_FETCH_CACHE_DURATION: ${{ secrets.FUNKWHALE_FETCH_CACHE_DURATION }} LASTFM_FETCH_CACHE_DURATION: ${{ secrets.LASTFM_FETCH_CACHE_DURATION }} INDIEKIT_URL: http://10.100.0.20:3000 FUNKWHALE_INSTANCE: http://10.100.0.40:5000 GITEA_URL: https://git.wildwuchs.work GITEA_INTERNAL_URL: http://127.0.0.1:3000 GITEA_ORG: svemagie.net OG_CACHE_DIR: /usr/local/git/.cache/og UNFURL_CACHE_DIR: /usr/local/git/.cache/unfurl UNFURL_TIMEOUT_MS: 5000 - name: Save eleventy-fetch cache run: | mkdir -p /usr/local/git/.cache/eleventy-fetch rsync -a --delete .cache/ /usr/local/git/.cache/eleventy-fetch/ - name: Deploy via rsync run: | cp .env _site/.env rsync -rl --delete \ --exclude='content/.indiekit/' \ --exclude='og/' \ --exclude='fonts/' \ -e "ssh -p 222" \ _site/ \ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/usr/local/bastille/jails/web/root/usr/local/www/blog/ - name: Sync OG images and fonts run: | rsync -rl --ignore-existing \ -e "ssh -p 222" \ _site/og/ \ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/usr/local/bastille/jails/web/root/usr/local/www/blog/og/ rsync -rl --ignore-existing \ -e "ssh -p 222" \ _site/fonts/ \ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/usr/local/bastille/jails/web/root/usr/local/www/blog/fonts/ - name: Trigger syndication webhook run: | # Script reads SECRET+SITE_URL from /usr/local/indiekit/.env directly — no env passing needed. ssh -p 222 -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no \ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \ "doas bastille cmd node sh /usr/local/indiekit/syndicate-webhook.sh"