refresh: rebuild a gh-pages venture in place, from its own tools/build.sh #67

Merged
teahubxyz merged 1 commits from refresh-in-place into main 2026-10-16 05:18:37 +00:00
2 changed files with 96 additions and 0 deletions
Showing only changes of commit 9eb99bf358 - Show all commits

No files matched your search

+95
View File
@@ -0,0 +1,95 @@
name: refresh
# Refresh a venture that lives on gh-pages and makes some of its own files (fonts, art, screenshots, generated
# code): run ventures/<slug>/tools/build.sh on the runner, in place, then commit what changed to gh-pages in one
# commit and ask Teahub pages to pull. Actions -> refresh -> Run workflow.
# `from` takes the venture's folder from another branch (say, a rework in progress), so the new version is built and
# lands whole instead of file by file. The build runs in a checkout without credentials; only ventures/<slug>/ comes
# back. Chrome for screenshots: CHROME (Playwright's Chromium is installed when the runner has none), with
# CHROME_FLAGS for running as root.
on:
workflow_dispatch:
inputs:
slug:
description: "The venture: ventures/<slug>/ on gh-pages, with a tools/build.sh"
required: true
type: string
from:
description: "Branch to take the venture's folder from (empty: gh-pages itself)"
required: false
type: string
permissions:
contents: write
concurrency:
group: venture # the venture loop and seed write gh-pages too: take turns
cancel-in-progress: false
jobs:
refresh:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: check the inputs
env: # inputs reach the scripts through the environment, never interpolated into them
SLUG: ${{ inputs.slug }}
FROM: ${{ inputs.from }}
run: |
printf '%s' "$SLUG" | grep -Eqx '[a-z0-9]([a-z0-9-]{0,40}[a-z0-9])?' || { echo "::error::bad slug: $SLUG"; exit 1; }
[ -z "$FROM" ] || printf '%s' "$FROM" | grep -Eqx '[A-Za-z0-9._/-]{1,100}' || { echo "::error::bad branch: $FROM"; exit 1; }
- uses: actions/checkout@v5
with:
ref: ${{ inputs.from || 'gh-pages' }}
path: work
fetch-depth: 1
sparse-checkout: ventures/${{ inputs.slug }}
persist-credentials: false # the build that runs next must not find a token in .git/config
- uses: actions/setup-node@v4
with:
node-version: 22
- name: find Chrome, or install Playwright's Chromium
run: |
set -eu
for c in google-chrome google-chrome-stable chromium chromium-browser; do
if command -v "$c" >/dev/null 2>&1; then
echo "CHROME=$(command -v "$c")" >> "$GITHUB_ENV"; echo "using $(command -v "$c")"; exit 0
fi
done
echo "no Chrome on this runner: installing Playwright's Chromium"
npx -y playwright@1.55.0 install --with-deps chromium > /tmp/pw.log 2>&1 || { tail -20 /tmp/pw.log; exit 1; }
c=$(find "$HOME/.cache/ms-playwright" -type f -name chrome -path '*chrome-linux*' | head -1)
[ -n "$c" ] && [ -x "$c" ] || { echo "::error::no Chromium after the install"; exit 1; }
echo "CHROME=$c" >> "$GITHUB_ENV"; echo "using $c"
- name: build
env:
SLUG: ${{ inputs.slug }}
CHROME_FLAGS: "--no-sandbox --disable-dev-shm-usage --disable-gpu"
run: |
set -eu
[ -f "work/ventures/$SLUG/tools/build.sh" ] || { echo "::error::ventures/$SLUG has no tools/build.sh"; exit 1; }
sh "work/ventures/$SLUG/tools/build.sh"
- uses: actions/checkout@v5
with:
ref: gh-pages
path: pages
fetch-depth: 1
- name: commit what changed
env:
SLUG: ${{ inputs.slug }}
FROM: ${{ inputs.from }}
run: |
set -eu
[ -f "pages/ventures/ventures.json" ] || { echo "::error::no ledger on gh-pages"; exit 1; }
rm -rf "pages/ventures/$SLUG"
cp -R "work/ventures/$SLUG" "pages/ventures/$SLUG"
find "pages/ventures/$SLUG" -name __pycache__ -type d -prune -exec rm -rf {} +
cd pages
git add --all "ventures/$SLUG"
if git diff --cached --quiet; then echo "nothing changed in ventures/$SLUG"; exit 0; fi
git diff --cached --stat | tail -25
git -c user.name='github-actions[bot]' -c user.email='github-actions[bot]@users.noreply.teahub.xyz' \
commit -q -m "ventures: refresh $SLUG${FROM:+ from $FROM}"
if ! git push -q origin HEAD:gh-pages; then # gh-pages moved under us: replay this one commit on top
git pull -q --rebase origin gh-pages
git push -q origin HEAD:gh-pages
fi
echo "refreshed: ventures/$SLUG -> https://$SLUG.yolopreneur.lol/"
- name: tell Teahub pages to pull
run: curl -fsS -X POST "https://yolopreneur.lol/_hook/${GITHUB_REPOSITORY}" || echo "pages pulls by itself within five minutes"
+1
View File
@@ -9,6 +9,7 @@ A Gitea-native agent factory, running on Teahub prod (teahub.xyz). The Gitea rep
- **Venture loop** (`venture.yml`, every 20 min once `TIME_VENTURE=on`): one venture card a day, or one right away from Actions → venture → Run workflow → "Start a new venture now". It scouts the repos that gained the most stars this week; seats pitch; the board votes (Borda, quorum of two); every board seat writes a launch plan (positioning, first users, pricing, channels, 7-day success and kill lines) and one seat synthesizes them into `LAUNCH.md`; the worker builds the product as the page on `gh-pages` (the tool fills the first screen under a slim header with the one signup button; pricing, limits and the why sit below); deterministic checks and headless Chrome (phone and desktop) gate it, with two fix rounds before an andon; GitHub Pages ships it at `/ventures/<slug>/`. Signups go through an issue form (`.github/ISSUE_TEMPLATE/early-access.yml`), each one thanked on its issue; after 7 days the count of distinct people decides: build v2, kill, or watch a week more. The studio index lives at `/ventures/`. Launch posts are drafts for a human; nothing is posted outside the repo.
- **Batches** (`TIME_VENTURE_BATCH=3`, or Run workflow → "batch"): one scout pass, a field of pitches, and the board's top k become venture cards in one milestone, built side by side and launched together. A held child gets one batch retry before it's counted dead; a retro compares the cohort once every child is measured.
- **Seed** (`seed.yml`, manual): land a venture that was built somewhere else. Give it a slug, the URL of a .tgz whose top folder is that slug (or of its parts, in order) and the .tgz's sha256, plus a name and a one-liner for a new venture. `seed.py` checks the bundle, copies it to `ventures/<slug>/` on `gh-pages` in one commit, adds or refreshes its line in `ventures/ventures.json`, and asks Teahub pages to pull, so it is live at `<slug>.yolopreneur.lol` within the minute. Nothing in the bundle runs.
- **Refresh** (`refresh.yml`, manual): for a venture that lives on gh-pages and makes some of its own files (fonts, art, screenshots, generated code). It runs `ventures/<slug>/tools/build.sh` on the runner, in place and without credentials, then commits what changed to gh-pages in one commit and asks Teahub pages to pull. `from` takes the folder from another branch first, so a reworked venture lands whole.
- **Backends** (optional, `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID`): each venture also gets its own Cloudflare Worker, D1 database and Workers AI, serving its Pages folder at `<slug>.<TIME_VENTURE_DOMAIN>`. The page talks to it through `time.js` (share links that save a creation, a rate-limited small model, and accounts with passkeys: no passwords, no email); every call degrades gracefully, so products still work on Pages alone. Code: `.github/time/cloudflare.py` and `cloudflare/worker.js`.
- **Doctor** (`doctor.yml`, manual): runs the tests and checks label, settings, keys and today's spend.
- **Update** (`update.yml`, weekly or manual): opens a PR with the latest engine from teahubxyz/time, so every project stays on one Time instead of drifting copies.