diff --git a/.github/workflows/refresh.yml b/.github/workflows/refresh.yml new file mode 100644 index 0000000..6e79ecf --- /dev/null +++ b/.github/workflows/refresh.yml @@ -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//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// 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// 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" diff --git a/README.md b/README.md index 317860f..714f678 100644 --- a/README.md +++ b/README.md @@ -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//`. 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//` 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 `.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//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 `.`. 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.