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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .github/workflows/deploy-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
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/<N>/` on
# the same branch — see `preview-pr.yml`. `keep_files: true` preserves
# those subdirectories when main republishes.
#
# Both workflows share the `gh-pages-write` concurrency group so they
# can't push to `gh-pages` at the same time and clobber each other.
#
# `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: gh-pages-write
cancel-in-progress: false

defaults:
run:
working-directory: site

jobs:
build-and-deploy:
runs-on: ubuntu-latest
timeout-minutes: 10
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 lint

- run: pnpm lint:ts

- run: pnpm build

- name: Verify hero text matches SITE_ORIGIN (no subpath leak)
run: |
HOST="${SITE_ORIGIN#https://}"
HOST="${HOST#http://}"
# Single exact-match check: the hero pill text is wrapped by
# tag boundaries, so requiring `>...<` rejects both a missing
# match AND any subpath leak (`/pr/N`, etc.) that would change
# the content between the tags.
if ! grep -qF ">Read ${HOST} before you start building on Stellar.<" out/index.html; then
echo "::error::Hero pill text in out/index.html doesn't exactly match SITE_ORIGIN=${SITE_ORIGIN}"
exit 1
fi

# peaceiris/actions-gh-pages@v4.1.0 pinned to commit SHA for
# supply-chain safety. Bump intentionally when reviewing release
# notes. Dependabot can automate this once `github-actions`
# updates are enabled.
- name: Publish to gh-pages
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: site/out
publish_branch: gh-pages
# Don't wipe pr/<N>/ subdirectories owned by preview-pr.yml.
keep_files: true
commit_message: "deploy: main @ ${{ github.sha }}"
120 changes: 120 additions & 0 deletions .github/workflows/preview-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: Deploy PR preview

# Internal-PR-only preview deployments. Each PR gets its own
# subdirectory on the `gh-pages` branch (`pr/<N>/`) 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.
#
# Concurrency: both this workflow and `deploy-pages.yml` use the shared
# `gh-pages-write` group so two simultaneous pushes to `gh-pages` can't
# collide. `cancel-in-progress: false` keeps each preview build to
# completion; PRs in rapid succession queue rather than thrash.

on:
pull_request:
types: [opened, reopened, synchronize, closed]
branches: [main]

permissions:
contents: write
pull-requests: write

concurrency:
group: gh-pages-write
cancel-in-progress: false

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
timeout-minutes: 10
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/<N>/.
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:
# Explicitly check out the PR head, not the auto-generated merge
# commit. The merge commit can drift from the PR's actual state
# (e.g., when base advances), so building head.sha makes the
# preview match exactly what was pushed to the PR.
- if: github.event.action != 'closed'
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}

# On `closed` events we don't build. We just need a checkout for
# the cleanup step to run from.
- if: github.event.action == 'closed'
uses: actions/checkout@v4

- 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 lint

- if: github.event.action != 'closed'
run: pnpm lint:ts

- 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://}"
# Single exact-match check: requiring tag boundaries on either
# side rejects subpath leaks (`pr/<N>/`, asset prefixes, etc.)
# that would otherwise pass a loose `grep -qF`.
if ! grep -qF ">Read ${HOST} before you start building on Stellar.<" out/index.html; then
echo "::error::Hero pill text in out/index.html doesn't exactly match SITE_ORIGIN=${SITE_ORIGIN}"
exit 1
fi

# rossjrw/pr-preview-action@v1.8.1 pinned to commit SHA for
# supply-chain safety. Bump intentionally when reviewing release
# notes. Dependabot can automate this once `github-actions`
# updates are enabled.
- name: Deploy / remove preview
uses: rossjrw/pr-preview-action@ffa7509e91a3ec8dfc2e5536c4d5c1acdf7a6de9
with:
source-dir: site/out
preview-branch: gh-pages
umbrella-dir: pr
action: auto
56 changes: 56 additions & 0 deletions site/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Site env variables.
#
# Local dev needs none of these — `pnpm dev` works with all defaults.
# To override locally, copy this file to `.env.local` (gitignored) and
# uncomment the values you want to set.
#
# README.md → "Build-time env vars" is the canonical reference; mirror
# changes there when you edit this file.

# -----------------------------------------------------------------------------
# Site origin and base path
# -----------------------------------------------------------------------------

# Canonical public origin used in displayed/copied URLs, sitemap, and
# llms.txt. Default: http://localhost:3000.
#
# SITE_ORIGIN=https://skills.stellar.org

# Next.js asset path prefix. Set automatically by the deploy workflows to
# match the GitHub Pages project subpath; unset for custom-domain
# deployments. Unset locally.
#
# NEXT_BASE_PATH=/stellar-dev-skill

# -----------------------------------------------------------------------------
# Preview build flags (set by preview-pr.yml, not for local use)
# -----------------------------------------------------------------------------

# Renders the "PR preview" banner and forces noindex/nofollow when "true".
#
# IS_PREVIEW=true

# PR number for the banner link.
#
# GITHUB_PR_NUMBER=42

# Git ref used in card "view source" links. Default: "main".
#
# GITHUB_SOURCE_REF=feature-branch

# owner/repo for "view source" links and the preview banner. The GitHub
# Actions runner sets this automatically; the upstream repo is the default
# locally.
#
# GITHUB_REPOSITORY=stellar/stellar-dev-skill

# -----------------------------------------------------------------------------
# Analytics
# -----------------------------------------------------------------------------

# Set to "true" to disable Google Tag Manager in production builds. GTM
# is loaded via @next/third-parties in src/app/layout.tsx. In development
# (NODE_ENV !== "production") and in PR previews (IS_PREVIEW=true) GTM is
# always disabled regardless of this flag.
#
# NEXT_PUBLIC_DISABLE_GOOGLE_ANALYTICS=true
13 changes: 13 additions & 0 deletions site/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"next/core-web-vitals",
"prettier"
],
"rules": {
"@typescript-eslint/no-explicit-any": "warn",
"import/first": "error",
"import/named": "off"
}
}
53 changes: 53 additions & 0 deletions site/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
package-lock.json

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# typescript
*.tsbuildinfo
next-env.d.ts

# test files
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

# ide settings
/.idea/
/.vscode/
/.kiro/

# versions
mise.toml

# copied at build time from ../skills/
/public/skills/

# generated at build time from src/data/skills.ts
/public/llms.txt
11 changes: 11 additions & 0 deletions site/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"bracketSpacing": true,
"bracketSameLine": false,
"printWidth": 80,
"proseWrap": "always",
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false
}
Loading