Recitation 9: CI/CD with Vercel¶
Today you'll put a game on the internet, change it safely, and stop a broken change from going live. You'll fork the classic 2048 game, deploy it with Vercel, redesign it with AI, and add a test that GitHub runs on every pull request.
- Part 1 (~10 min): deploy your game to a live URL.
- Part 2 (~15 min): redesign it on a branch, and preview the change before it goes live.
- Part 3 (~15 min): add CI, so that a broken change can't reach your live site.
After the recitation, remember to complete the Gradescope assignment to receive credit!
How the pieces fit¶
git push
│
▼
GitHub ──┬──▶ CI (Part 3): GitHub Actions runs your tests
│ → ✅ or ❌ on the pull request
│
└──▶ CD (Parts 1–2): Vercel builds and deploys
→ master branch: production URL
→ other branches: preview URL
| Term | Meaning |
|---|---|
| Continuous integration (CI) | Automatically testing every change, so problems surface before the change merges. |
| Continuous deployment (CD) | Automatically deploying every change that lands on the main branch to the live site. |
| Production deployment | The live site everyone sees, built from master. |
| Preview deployment | A separate copy of the site, built from another branch or a pull request, with its own URL. Production is untouched. |
| Status check | A pass/fail result, such as a test run or a Vercel build, that GitHub shows on a pull request. |
| Ruleset | A repository setting that blocks merging into master unless the required checks pass. |
Before recitation: setup (5 min)¶
- Check that you can sign in to GitHub and that
git --versionworks in your terminal. - Go to vercel.com/signup.
- Choose the Hobby plan, enter your name, and click Continue.
- Click Continue with GitHub and authorize Vercel.
- Have an AI coding assistant ready.
Check: you land on your Vercel dashboard.
Part 1: Deploy your game (~10 min)¶
Step 1: Fork the repository¶
- Open github.com/gabrielecirulli/2048.
- Click Fork (top right).
- Leave every setting as it is, including Copy the
masterbranch only. - Click Create fork.
Check: the page now shows <your-username>/2048, with "forked from gabrielecirulli/2048" underneath.
Welcome to the real world. Until now, your course repos have been cozy sandboxes where only your TAs could see your mistakes. Today you're forking the real 2048, starred by more than 13,000 people. Open your pull request against the original project by accident, and its author gets a surprise redesign proposal while the whole internet watches. It's completely harmless, and you can close it with one click, but Step 5 shows you how to keep your masterpiece to yourself. (One of your TAs did exactly this while testing this recitation. The pull request is still there for all to see.)
Step 2: Deploy on Vercel¶
- Go to vercel.com/new.
- Under Import Git Repository, find
2048and click Import.- Don't see it? Click Adjust GitHub App Permissions (or Install), choose Only select repositories, select
2048, and click Save. You'll come back to this page.
- Don't see it? Click Adjust GitHub App Permissions (or Install), choose Only select repositories, select
- In Project Name, type
2048-<andrewid>(for example,2048-smtiwari). This becomes your URL. - Leave everything else as it is. Framework Preset should say Other.
- Click Deploy and wait about a minute.
- When the Congratulations! page appears, click the screenshot of your game to open it.
Check: your game loads at https://2048-<andrewid>.vercel.app, and the tiles move when you press the arrow keys.
What just happened? Vercel now watches your fork. Every time you push to
master, it redeploys this site by itself. That is continuous deployment.
Part 2: Preview a change (~15 min)¶
Step 3: Clone your fork and create a branch¶
- On your fork's GitHub page, click the green Code button and copy the HTTPS URL.
-
In your terminal, run:
git clone <the URL you copied> cd 2048 git switch -c restyle
Check: git branch --show-current prints restyle.
Why a branch? When you push any branch other than
master, Vercel builds a preview deployment: a separate copy of the site with its own URL. Your production site stays as it is.
Step 4: Make the game your own with AI¶
This is the fun part. Use an AI assistant to redesign the game however you like: the colors, fonts, background, tiles, title text, or a whole new theme. Get as creative as you want and have fun with it.
For example, Shrey, one of your TAs, is a big Tron: Legacy fan and gave the game a Tron: Legacy look.
- Open the
2048folder in your AI assistant. - Describe the look you want.
- Open
index.htmlin your browser (double-click it, or runopen index.htmlon macOS) to see the result. - Keep asking for changes and refreshing the page until you love it.
Check: your new design shows up in the browser, and the game still plays.
Step 5: Push your branch and open a pull request¶
-
In your terminal, inside the
2048folder, run:git add -A git commit -m "My new theme" git push -u origin restyle -
Open your fork's page on GitHub and click Compare & pull request in the yellow banner.
- No banner? Click the Pull requests tab, then New pull request.
- Important: GitHub points your pull request at the original project by default. Click the base repository dropdown and choose
<your-username>/2048, unless you want 13,000 stargazers reviewing your redesign. - Check that the top of the page now reads base: master ← compare: restyle.
- Give the pull request a title, such as the name of your theme.
- Click Create pull request.
- Wait up to a minute for the vercel bot to comment on your pull request.
- In the bot's comment, under Actions, click Preview.
- In another tab, open your production URL (
2048-<andrewid>.vercel.app).
Check: the preview shows your new design, while production still shows the original game.
Why previews? When you pushed
restyle, Vercel built a preview deployment of your branch with its own URL. A preview lets teammates and reviewers try a change before it reaches real users. Production only changes when the change is merged.
Step 6: Merge and watch production update¶
- At the bottom of your pull request, click Merge pull request, then Confirm merge.
- In Vercel, open your project and click the Deployments tab.
- Watch the newest deployment, labeled Production, until it says Ready.
- Refresh your production URL. If it still looks unchanged, hard-refresh with Cmd+Shift+R (macOS) or Ctrl+Shift+R (Windows).
Check: your production site now shows your new design.
What just happened? Merging put your change on
master, and Vercel deployedmasterto production with no extra steps. That's the full loop of continuous deployment: branch, preview, review, merge, production.
Part 3: Protect production with CI (~15 min)¶
So far, anything merged into master goes straight to production, even if it breaks the game. In this part, you add a test that GitHub runs on every pull request. Then you break the game on purpose and watch GitHub refuse to merge it.
Step 7: Add CI to your repository¶
- Finish Step 6 first. Your restyle pull request must be merged before you add CI.
-
In your terminal, inside the
2048folder, updatemaster:git switch master git pull -
Create a file named
.github/workflows/ci.ymlwith this content. It tells GitHub to run your test on every pull request and on every push tomaster.name: CI on: push: branches: [master] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v7 with: node-version: 22 - run: node --test test/game.test.js -
Create a file named
test/game.test.jswith this content. It checks one rule of the game: two 2s merge into a 4.// Checks one rule of 2048 against the real game code in js/. const test = require("node:test"); const assert = require("node:assert"); const fs = require("node:fs"); const vm = require("node:vm"); // Load the game's logic. These three files never touch the browser. const game = vm.createContext({}); for (const file of ["tile.js", "grid.js", "game_manager.js"]) { vm.runInContext(fs.readFileSync("js/" + file, "utf8"), game); } // A do-nothing stand-in for the keyboard, the screen, and storage. function Fake() {} for (const method of ["on", "actuate", "getGameState", "setGameState", "clearGameState", "getBestScore", "setBestScore"]) { Fake.prototype[method] = function () {}; } test("two 2s merge into a 4", () => { const manager = new game.GameManager(4, Fake, Fake, Fake); manager.grid = new game.Grid(4); // start from an empty board manager.grid.insertTile(new game.Tile({ x: 0, y: 0 }, 2)); manager.grid.insertTile(new game.Tile({ x: 1, y: 0 }, 2)); manager.move(3); // 3 means "left" assert.strictEqual(manager.grid.cells[0][0].value, 4); }); -
Run the test on your machine:
node --test test/game.test.jsYou should see output like this. The times will differ.
✔ two 2s merge into a 4 (1.687ms) ℹ tests 1 ℹ suites 0 ℹ pass 1 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 56.487084 -
Commit and push to
master:git add .github test git commit -m "Add CI" git push -
On GitHub, click your fork's Actions tab.
- Asked to enable workflows? Click the button, then trigger a run with
git commit --allow-empty -m "Run CI"andgit push.
- Asked to enable workflows? Click the button, then trigger a run with
Check: the test passes on your machine (pass 1), and the Actions tab shows a CI run with a green check mark.
What is CI? Continuous integration means every change is tested automatically, so problems show up before the change reaches
master. From now on, GitHub runs your test on every pull request.
Step 8: Require CI to pass before merging¶
- In your fork, click Settings.
- In the left sidebar, click Rules, then Rulesets.
- Click New ruleset, then New branch ruleset.
- For Ruleset Name, type
protect-master. - Set Enforcement status to Active.
- Under Target branches, click Add target, then Include default branch.
- Under Branch rules, check Require a pull request before merging. Leave Required approvals at 0.
- Check Require status checks to pass.
- In the Required status checks box, click Add checks, type
testin the search box, and click test in the list. - Leave the Bypass list empty, and click Create.
Check: protect-master appears in your list of rulesets as Active.
Why a rule? CI only reports results. The rule makes GitHub enforce them: nothing reaches
master, and so nothing reaches production, unlesstestpasses.
Step 9: Break the game on a branch¶
-
Create a branch:
git switch -c break-it -
Open
js/game_manager.jsand find this line (around line 157):var merged = new Tile(positions.next, tile.value * 2); -
Change
* 2to* 3, so that two 2s now merge into a 6. -
Run the test again:
node --test test/game.test.jsThis time it fails. The key line is
6 !== 4: the test expected a 4 and got a 6.✖ two 2s merge into a 4 (1.8265ms) ℹ tests 1 ℹ suites 0 ℹ pass 0 ℹ fail 1 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 49.815125 ✖ failing tests: test at test/game.test.js:20:2 ✖ two 2s merge into a 4 (1.8265ms) AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: 6 !== 4 at TestContext.<anonymous> (.../2048/test/game.test.js:28:11) at Test.runInAsyncScope (node:async_hooks:214:14) at Test.run (node:internal/test_runner/test:1047:25) at Test.start (node:internal/test_runner/test:944:17) at startSubtestAfterBootstrap (node:internal/test_runner/harness:297:17) { generatedMessage: true, code: 'ERR_ASSERTION', actual: 6, expected: 4, operator: 'strictEqual' } -
Commit and push anyway:
git commit -am "Bigger merges" git push -u origin break-it -
Open a pull request as in Step 5. Click Compare & pull request, set the base repository to
<your-username>/2048, and click Create pull request.
Check: the test check on your pull request shows a red ✗, and GitHub blocks the merge.
Step 10: See what CI caught¶
- Open the preview from the vercel bot's comment and merge two 2s. You get a 6.
- Back on the pull request, click Details next to the failing test check.
- Find the failed test,
two 2s merge into a 4. It expected 4 and got 6.
Discuss: Vercel still deployed a preview of the broken game. What kept it out of production?
What just happened? CI caught the broken rule before the merge, and your ruleset turned that red ✗ into a blocked merge. Production stays on the last good version.
Finished early?
- Fix the line, push, watch test turn green, and merge.
- Break the game a different way: delete the line
self.score += merged.value;so that merges stop adding to your score. Does CI catch it? Why not?