Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
176 changes: 1 addition & 175 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
name: Validate examples and publish recipes
name: Validate examples

on:
pull_request:
branches: [master]
push:
branches: [master]
workflow_dispatch:
inputs:
only:
description: "Publish only this recipe slug (optional)"
required: false

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: examples-${{ github.ref }}
Expand Down Expand Up @@ -95,171 +89,3 @@ jobs:
;;
esac
test -s "$artifact"

manifest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun test scripts/catalog.test.mjs
- uses: actions/configure-pages@v5
if: github.event_name != 'pull_request'
- uses: actions/upload-pages-artifact@v3
if: github.event_name != 'pull_request'
with:
path: dist

deploy-manifest:
needs: manifest
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy canonical JSON feed
id: deployment
uses: actions/deploy-pages@v4

discover-recipes:
runs-on: ubuntu-latest
outputs:
slugs: ${{ steps.list.outputs.slugs }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: list
env:
ONLY: ${{ inputs.only }}
BEFORE: ${{ github.event.before }}
run: |
if [ -n "$ONLY" ]; then
test -d "recipes/$ONLY"
jq -cn --arg slug "$ONLY" '[ $slug ]' | sed 's/^/slugs=/' >> "$GITHUB_OUTPUT"
exit 0
fi

all_slugs() {
find recipes -mindepth 1 -maxdepth 1 -type d -print \
| sed 's#^recipes/##' | sort | jq -R -s -c 'split("\n") | map(select(length > 0))'
}

if [ "${{ github.event_name }}" = "pull_request" ] \
|| [ "${{ github.event_name }}" = "workflow_dispatch" ] \
|| [ -z "$BEFORE" ] \
|| [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then
echo "slugs=$(all_slugs)" >> "$GITHUB_OUTPUT"
exit 0
fi

# A pure rename (R100) moves a file without changing what it serves.
changed=$(git diff --name-status -M "$BEFORE" "${{ github.sha }}" \
| awk -F'\t' '$1 != "R100" {print $NF}')
# A recipe directory that no longer exists has nothing to publish.
slugs=$(printf '%s\n' "$changed" | sed -n 's#^recipes/\([^/]*\)/site/.*#\1#p' \
| sort -u | while read -r slug; do if [ -d "recipes/$slug" ]; then echo "$slug"; fi; done \
| jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "slugs=$slugs" >> "$GITHUB_OUTPUT"

publish-recipes:
needs: discover-recipes
if: needs.discover-recipes.outputs.slugs != '[]'
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 4
matrix:
slug: ${{ fromJson(needs.discover-recipes.outputs.slugs) }}
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Install Spacefast CLI
run: bun add -g spacefast@0.8.0
- name: Build
working-directory: recipes/${{ matrix.slug }}
run: |
runtime=$(jq -r '.runtime // "static"' meta.json)
if [ "$runtime" = "zero" ]; then
cd site
bun install --frozen-lockfile
if jq -e '.scripts.build' package.json >/dev/null; then
bun run build
fi
if [ -f dist/sf.jsonc ]; then
# Framework layout: the build script staged the capsule into
# dist/ and already ran `sf build dist` (e.g. astro-zero).
test -f dist/.spacefast/zero/public/index.html
else
sf build
test -f .spacefast/zero/public/index.html
fi
if jq -e '.scripts.test' package.json >/dev/null; then
bun run test
fi
elif [ "$runtime" = "functions" ]; then
cd site
bun install --frozen-lockfile
# Sanity build only: `sf publish` runs its own OpenNext build.
bun run build
elif [ -f site/package.json ]; then
cd site
bun install --frozen-lockfile || bun install
bun run build
fi
- name: Resolve and validate publish directory
id: dir
working-directory: recipes/${{ matrix.slug }}
run: |
runtime=$(jq -r '.runtime // "static"' meta.json)
if [ "$runtime" = "zero" ] && [ -f site/dist/sf.jsonc ]; then
dir=site/dist
elif [ "$runtime" = "zero" ] || [ "$runtime" = "functions" ]; then
dir=site
elif [ -d site/dist ]; then dir=site/dist
elif [ -d site/build ]; then dir=site/build
elif [ -d site/out ]; then dir=site/out
else dir=site
fi
echo "dir=$dir" >> "$GITHUB_OUTPUT"
if [ "$runtime" = "static" ]; then
bun ../../scripts/validate-publish-dir.mjs "${{ matrix.slug }}" "$dir"
fi
- name: Publish Spacefast space
if: github.event_name != 'pull_request'
working-directory: recipes/${{ matrix.slug }}
env:
SPACEFAST_TOKEN: ${{ secrets.SPACEFAST_DEPLOY_KEY }}
SPACEFAST_API_URL: https://api.spacefast.com
run: |
space_slug=$(jq -er '.publish_slug // .slug' meta.json)
runtime=$(jq -r '.runtime // "static"' meta.json)
publish_args=(
"${{ steps.dir.outputs.dir }}"
--team spacefast-examples
--source-type git
--git-repository "${{ github.repository }}"
--git-branch "${{ github.ref_name }}"
--git-commit "${{ github.sha }}"
--wait
--json
)
if [ "$runtime" = "zero" ]; then
# The Build step already ran the framework build and `sf build`.
publish_args+=(--skip-build)
fi

set +e
result=$(sf publish "${publish_args[@]}" --space "$space_slug" 2>&1)
exit_code=$?
set -e
printf '%s\n' "$result"

if [ "$exit_code" -eq 0 ]; then
exit 0
fi
if ! jq -e '.error.code == "not_found"' <<<"$result" >/dev/null; then
exit "$exit_code"
fi

sf publish "${publish_args[@]}" --slug "$space_slug"
24 changes: 6 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ server. `astro-zero/` needs both: Astro's dev server for the pages and
| [`wp-react/`](./wp-react/) | A Vite + React SPA reading WordPress posts in the browser, with the `_redirects` SPA fallback |
| [`unfurl/`](./unfurl/) | Next.js SSR, a route handler, and middleware on the Functions runtime |
| [`trailhead/`](./trailhead/) | A Next.js static export with `@spacefast/image` Site Accelerator images, `_redirects`, and `_headers` |
| [`recipes/`](./recipes/) | Canonical prompts, metadata, live outputs, and the public recipe feed |
| [`kitchen-sink/`](./kitchen-sink/) | Zero, Pages, PHP, JavaScript, TypeScript, auth, mail, and routing in one project |
| [`migrate-from-cloudflare-pages/`](./migrate-from-cloudflare-pages/) | Wrangler build settings and Pages convention files |
| [`migrate-from-vercel/`](./migrate-from-vercel/) | `vercel.json` build, redirects, rewrites, headers, and cron import |
Expand All @@ -47,22 +46,11 @@ server. `astro-zero/` needs both: Astro's dev server for the pages and
Each example README calls out the important files, the expected build output,
and any product boundary that cannot be translated automatically.

## Recipes integration

`recipes/` is the source of truth for [spacefast.com/recipes](https://spacefast.com/recipes).
Each recipe contains its copy-paste prompt, gallery metadata, runnable output,
and live URL. GitHub Actions compiles those records into the historical public
feed at <https://spacefast.github.io/examples/manifest.json> and publishes only
changed recipe outputs.

Do not duplicate recipe prompts in another package or repository. Framework
examples teach an integration; recipes are user-facing starting points that an
agent can customize and publish.

## Validation

The pull-request workflow builds every top-level project through `sf build`,
runs the Zero app tests, validates every recipe output, and builds the recipe
manifest, without creating a live version. `migrate-from-netlify` builds its
prebuilt output, because `sf build` has no flag to accept the Netlify features
that still need a port.
The pull-request workflow builds every top-level project through `sf build`
and runs the Zero app tests, without creating a live version.
`migrate-from-netlify` builds its prebuilt output, because `sf build` has no
flag to accept the Netlify features that still need a port.

Recipe prompts live in their own repository and are published separately.
24 changes: 0 additions & 24 deletions recipes/2048/README.md

This file was deleted.

26 changes: 0 additions & 26 deletions recipes/2048/meta.json

This file was deleted.

59 changes: 0 additions & 59 deletions recipes/2048/prompt.md

This file was deleted.

Loading
Loading