From 13463fd16afc2749a5a79c25f2d8a76b7b78bde0 Mon Sep 17 00:00:00 2001 From: oceans404 Date: Wed, 13 May 2026 13:40:10 -0700 Subject: [PATCH 1/3] Add skills.stellar.org landing site + GitHub Pages workflows A Next.js 15 static-export site at site/ that mirrors every SKILL.md under skills/ and exposes them as a copy-pastable directory at the deploy origin, plus a /llms.txt index for AI agents. GitHub Pages deploy is branch-based via two workflows at .github/: - deploy-pages.yml publishes main to gh-pages root on push to main. - preview-pr.yml publishes PR previews to gh-pages:/pr// with a bot comment containing the URL; cleans up on PR close. Both workflows pin SITE_ORIGIN to production so the hero pill, copy-pastable card URLs, and llms.txt show post-merge URLs even on preview builds. An IS_PREVIEW banner identifies preview builds. Custom-domain cutover is a one-place repo-variable change. Co-Authored-By: mk <86380734+minkyeongshin@users.noreply.github.com> --- .github/workflows/deploy-pages.yml | 74 + .github/workflows/preview-pr.yml | 99 + site/.env.example | 25 + site/.eslintrc.json | 12 + site/.gitignore | 53 + site/.prettierrc.json | 11 + site/CLAUDE.md | 130 + site/README.md | 184 + site/next.config.js | 24 + site/package.json | 41 + site/pnpm-lock.yaml | 3671 +++++++++++++++++ site/scripts/copy-skills.mjs | 99 + site/scripts/generate-llms-txt.mjs | 174 + site/src/app/_components/CopyButton.tsx | 69 + site/src/app/_components/SkillCard.tsx | 57 + site/src/app/_components/SkillsFilter.tsx | 115 + .../src/app/_components/ThemeSwitchIsland.tsx | 20 + site/src/app/_components/icons.tsx | 71 + site/src/app/apple-icon.png | Bin 0 -> 2580 bytes site/src/app/error.tsx | 51 + site/src/app/favicon.ico | Bin 0 -> 4286 bytes site/src/app/global-error.tsx | 13 + site/src/app/icon.svg | 8 + site/src/app/icon1.png | Bin 0 -> 2667 bytes site/src/app/icon2.png | Bin 0 -> 7608 bytes site/src/app/layout.tsx | 30 + site/src/app/not-found.tsx | 23 + site/src/app/page.tsx | 229 + site/src/app/styles.scss | 654 +++ site/src/data/installers.mjs | 44 + site/src/data/skills.ts | 173 + site/src/lib/skill-meta.mjs | 60 + site/src/styles/globals.scss | 75 + site/src/styles/utils.scss | 12 + site/tsconfig.json | 32 + 35 files changed, 6333 insertions(+) create mode 100644 .github/workflows/deploy-pages.yml create mode 100644 .github/workflows/preview-pr.yml create mode 100644 site/.env.example create mode 100644 site/.eslintrc.json create mode 100644 site/.gitignore create mode 100644 site/.prettierrc.json create mode 100644 site/CLAUDE.md create mode 100644 site/README.md create mode 100644 site/next.config.js create mode 100644 site/package.json create mode 100644 site/pnpm-lock.yaml create mode 100644 site/scripts/copy-skills.mjs create mode 100644 site/scripts/generate-llms-txt.mjs create mode 100644 site/src/app/_components/CopyButton.tsx create mode 100644 site/src/app/_components/SkillCard.tsx create mode 100644 site/src/app/_components/SkillsFilter.tsx create mode 100644 site/src/app/_components/ThemeSwitchIsland.tsx create mode 100644 site/src/app/_components/icons.tsx create mode 100644 site/src/app/apple-icon.png create mode 100644 site/src/app/error.tsx create mode 100644 site/src/app/favicon.ico create mode 100644 site/src/app/global-error.tsx create mode 100644 site/src/app/icon.svg create mode 100644 site/src/app/icon1.png create mode 100644 site/src/app/icon2.png create mode 100644 site/src/app/layout.tsx create mode 100644 site/src/app/not-found.tsx create mode 100644 site/src/app/page.tsx create mode 100644 site/src/app/styles.scss create mode 100644 site/src/data/installers.mjs create mode 100644 site/src/data/skills.ts create mode 100644 site/src/lib/skill-meta.mjs create mode 100644 site/src/styles/globals.scss create mode 100644 site/src/styles/utils.scss create mode 100644 site/tsconfig.json diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml new file mode 100644 index 0000000..7fb26ad --- /dev/null +++ b/.github/workflows/deploy-pages.yml @@ -0,0 +1,74 @@ +name: Deploy site to GitHub Pages + +# Builds the Next.js static export from `site/` and publishes it to the +# `gh-pages` branch (project root). PR previews live under `pr//` on +# the same branch — see `preview-pr.yml`. `keep_files: true` preserves +# those subdirectories when main republishes. +# +# `SITE_ORIGIN` and `SITE_BASE_PATH` default to the project-Pages URL +# derived from repo metadata. Override via repo variables +# (Settings → Secrets and variables → Actions → Variables) when cutting +# over to a custom domain — both workflows read the same vars, so a +# single edit keeps main and previews in lockstep. + +on: + push: + branches: [main] + paths: + - "skills/**" + - "site/**" + - ".github/workflows/deploy-pages.yml" + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: pages-main + cancel-in-progress: false + +defaults: + run: + working-directory: site + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + env: + SITE_ORIGIN: ${{ vars.SITE_ORIGIN || format('https://{0}.github.io/{1}', github.repository_owner, github.event.repository.name) }} + NEXT_BASE_PATH: ${{ vars.SITE_BASE_PATH || format('/{0}', github.event.repository.name) }} + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10.15.1 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + cache-dependency-path: site/pnpm-lock.yaml + + - run: pnpm install --frozen-lockfile + + - run: pnpm build + + - name: Verify hero text matches SITE_ORIGIN + run: | + HOST="${SITE_ORIGIN#https://}" + HOST="${HOST#http://}" + if ! grep -qF "Read ${HOST} before you start building on Stellar" out/index.html; then + echo "::error::Hero text in out/index.html doesn't match SITE_ORIGIN=${SITE_ORIGIN}" + exit 1 + fi + + - name: Publish to gh-pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: site/out + publish_branch: gh-pages + # Don't wipe pr// subdirectories owned by preview-pr.yml. + keep_files: true + commit_message: "deploy: main @ ${{ github.sha }}" diff --git a/.github/workflows/preview-pr.yml b/.github/workflows/preview-pr.yml new file mode 100644 index 0000000..20e1ae7 --- /dev/null +++ b/.github/workflows/preview-pr.yml @@ -0,0 +1,99 @@ +name: Deploy PR preview + +# Internal-PR-only preview deployments. Each PR gets its own +# subdirectory on the `gh-pages` branch (`pr//`) and a comment with +# the URL. The same workflow tears the subdirectory down when the PR +# closes, so previews don't accumulate. +# +# `SITE_ORIGIN` deliberately matches main (production), NOT the preview +# URL. The hero pill, copy-pastable card URLs, and `llms.txt` show what +# users will see after merge — preview URLs would die when the PR +# closes and poison anything that copied them. Only `NEXT_BASE_PATH` +# varies, so Next.js routes assets to the preview subpath while the +# displayed content stays canonical. `IS_PREVIEW` renders a banner so +# reviewers know what they're looking at. +# +# Fork PRs are intentionally skipped (see the `if:` guard below): they +# can't safely receive the repo's GITHUB_TOKEN, and our `peaceiris` + +# `rossjrw` steps need write access to push to `gh-pages` and comment. + +on: + pull_request: + types: [opened, reopened, synchronize, closed] + branches: [main] + +permissions: + contents: write + pull-requests: write + +concurrency: + group: pages-pr-${{ github.event.number }} + cancel-in-progress: true + +defaults: + run: + working-directory: site + +jobs: + preview: + # Skip PRs opened from forks (no write token, can't deploy). + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + env: + # Production origin (same as main) — see header comment. + SITE_ORIGIN: ${{ vars.SITE_ORIGIN || format('https://{0}.github.io/{1}', github.repository_owner, github.event.repository.name) }} + # Preview-scoped path so assets resolve under pr//. + NEXT_BASE_PATH: ${{ vars.SITE_BASE_PATH || format('/{0}', github.event.repository.name) }}/pr/${{ github.event.number }} + IS_PREVIEW: "true" + # PR head SHA so card "view source" links resolve for newly-added + # files that aren't on main yet. + GITHUB_SOURCE_REF: ${{ github.event.pull_request.head.sha }} + GITHUB_PR_NUMBER: ${{ github.event.number }} + steps: + - uses: actions/checkout@v4 + + # Build steps only run for open/reopen/synchronize — closing a PR + # just needs the cleanup step at the bottom. + - if: github.event.action != 'closed' + uses: pnpm/action-setup@v4 + with: + version: 10.15.1 + + - if: github.event.action != 'closed' + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + cache-dependency-path: site/pnpm-lock.yaml + + - if: github.event.action != 'closed' + run: pnpm install --frozen-lockfile + + - if: github.event.action != 'closed' + run: pnpm build + + - name: Verify hero text matches production SITE_ORIGIN (no PR path leak) + if: github.event.action != 'closed' + run: | + HOST="${SITE_ORIGIN#https://}" + HOST="${HOST#http://}" + if ! grep -qF "Read ${HOST} before you start building on Stellar" out/index.html; then + echo "::error::Hero text in out/index.html doesn't match production SITE_ORIGIN=${SITE_ORIGIN}" + exit 1 + fi + # Defensive: catch any future regression where SITE_ORIGIN + # accidentally varies per env and leaks the preview path into + # displayed text. Asset paths (in