diff --git a/.github/workflows/promote-staging.yml b/.github/workflows/promote-staging.yml
index 3cb89f3..57d0a4c 100644
--- a/.github/workflows/promote-staging.yml
+++ b/.github/workflows/promote-staging.yml
@@ -40,21 +40,39 @@ jobs:
echo "Commits to be promoted:"
echo "$COMMITS"
- # Skip if only chore/docs commits
- NON_CHORE=$(echo "$COMMITS" | grep -vE "^(chore|docs|ci|style|refactor)(\(.+\))?:" || true)
- if [ -z "$NON_CHORE" ]; then
- echo "Only chore/docs commits. Skipping version bump."
+ # Check if any extension files actually changed
+ EXTENSION_FILES=$(git diff --name-only origin/main..staging -- extension/ || true)
+ echo "Extension files changed:"
+ echo "$EXTENSION_FILES"
+
+ if [ -z "$EXTENSION_FILES" ]; then
+ echo "No extension files changed. Skipping version bump."
echo "should_bump=false" >> $GITHUB_OUTPUT
exit 0
fi
- # Determine bump type from conventional commits
+ # Filter out non-extension commits (chore, docs, landing page changes)
+ # These don't trigger version bumps
+ EXTENSION_COMMITS=$(echo "$COMMITS" | grep -vE "^(chore|docs|ci|style|refactor)(\(.+\))?:" | grep -vE "^(feat|fix)\(landing\):" || true)
+
+ if [ -z "$EXTENSION_COMMITS" ]; then
+ echo "Only chore/docs/landing commits. Skipping version bump."
+ echo "should_bump=false" >> $GITHUB_OUTPUT
+ exit 0
+ fi
+
+ echo "Extension-relevant commits:"
+ echo "$EXTENSION_COMMITS"
+
+ # Determine bump type from extension commits only
BUMP="patch" # Default
- if echo "$COMMITS" | grep -qE "^feat(\(.+\))?!:|BREAKING CHANGE:"; then
+ # Check for breaking changes (exclude landing scope)
+ if echo "$EXTENSION_COMMITS" | grep -qE "^feat(\([^)]*\))?!:|BREAKING CHANGE:"; then
BUMP="major"
echo "Found breaking change - major bump"
- elif echo "$COMMITS" | grep -qE "^feat(\(.+\))?:"; then
+ # Check for features (exclude landing scope)
+ elif echo "$EXTENSION_COMMITS" | grep -qE "^feat(\([^)]*\))?:"; then
BUMP="minor"
echo "Found feat commit - minor bump"
else
@@ -99,7 +117,7 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add extension/manifest.json
- git commit -m "chore: bump version to $NEW_VERSION [skip ci]"
+ git commit -m "chore: bump version to $NEW_VERSION"
git push origin staging
echo "Bumped version to $NEW_VERSION on staging"
diff --git a/.github/workflows/publish-extension.yml b/.github/workflows/publish-extension.yml
index be4e5e9..988adfe 100644
--- a/.github/workflows/publish-extension.yml
+++ b/.github/workflows/publish-extension.yml
@@ -56,11 +56,12 @@ jobs:
git push origin "$TAG"
# Get recent commits for release notes (from previous tag)
+ # Filter out: merge commits, chore, docs, ci, style, refactor
LAST_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "")
if [ -n "$LAST_TAG" ]; then
- NOTES=$(git log $LAST_TAG..HEAD --pretty=format:"- %s" | grep -vE "^- chore:" | head -20)
+ NOTES=$(git log $LAST_TAG..HEAD --pretty=format:"- %s" | grep -vE "^- (Merge|chore|docs|ci|style|refactor)" | head -20)
else
- NOTES=$(git log --pretty=format:"- %s" -10 | grep -vE "^- chore:")
+ NOTES=$(git log --pretty=format:"- %s" -10 | grep -vE "^- (Merge|chore|docs|ci|style|refactor)")
fi
# Write release notes to temp file to avoid YAML parsing issues
@@ -111,3 +112,23 @@ jobs:
client-secret: ${{ secrets.CHROME_CLIENT_SECRET }}
refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }}
publish: true
+
+ sync-staging:
+ needs: [tag-and-release, build-and-publish]
+ if: always() && needs.tag-and-release.result == 'success'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: staging
+ fetch-depth: 0
+ token: ${{ secrets.STAGING_PAT }}
+
+ - name: Sync staging with main
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git fetch origin main
+ git merge origin/main -m "chore: sync staging with main after release"
+ git push origin staging
+ echo "✅ Staging synced with main"
diff --git a/CLAUDE.md b/CLAUDE.md
index f1a62a3..671b1ec 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -183,15 +183,32 @@ gh pr create --base staging --title "feat: add new feature" --body "Description"
### Conventional Commits (Auto-Versioning)
-Version bumps are automatic based on commit message prefixes:
+Version bumps are automatic based on commit message prefixes.
+
+**CI enforces conventional commit format** - PRs with invalid messages will be rejected.
| Prefix | Version Bump | Example |
|--------|--------------|---------|
| `fix:` | Patch (0.0.X) | `fix: resolve scroll issue` |
| `feat:` | Minor (0.X.0) | `feat: add dark mode` |
| `feat!:` | Major (X.0.0) | `feat!: redesign entire UI` |
-| `chore:` | No bump | `chore: update docs` |
+| `chore:` | No bump | `chore: update deps` |
| `docs:` | No bump | `docs: fix typo in README` |
+| `ci:` | No bump | `ci: update workflow` |
+| `style:` | No bump | `style: format code` |
+| `refactor:` | No bump | `refactor: extract helper` |
+| `test:` | No bump | `test: add unit tests` |
+
+### Landing Page Changes
+
+Use `(landing)` scope for landing page changes - **these don't trigger Chrome extension releases**:
+
+| Commit | Version Bump | Releases Extension? |
+|--------|--------------|---------------------|
+| `feat(landing): redesign hero` | None | No |
+| `fix(landing): mobile layout` | None | No |
+| `feat: add dark mode` | Minor | Yes |
+| `fix: resolve quiz crash` | Patch | Yes |
### CI/CD Pipeline
@@ -220,16 +237,36 @@ feature/* ──PR──→ staging ──────────────
1. Create `hotfix/*` branch from main
2. Make fix AND bump version in `extension/manifest.json` manually
3. PR to main → CI validates version was bumped
-4. Merge → auto-publishes like normal release
+4. Merge → auto-publishes like normal release + auto-syncs staging
+
+**Manual promotion (don't want to wait for 6pm):**
+1. Go to: Actions → "Promote Staging to Main"
+2. Click "Run workflow" → Select branch: `staging` → Run
+3. Same process as daily, just triggered manually
### Workflows
| File | Trigger | Purpose |
|------|---------|---------|
-| `ci.yml` | PR to main/staging | Validate branch, run tests, check hotfix version |
-| `publish-extension.yml` | Push to main | Tag, release, publish (reads version from manifest) |
+| `ci.yml` | PR to main/staging | Validate branch, lint commits, run tests, check hotfix version |
+| `publish-extension.yml` | Push to main | Tag, release, publish, sync staging with main |
| `promote-staging.yml` | Daily 6pm EST / manual | Bump version on staging, PR to main |
+**CI checks (`ci.yml`):**
+- `check-branch-name` - Validates branch naming (feature/*, fix/* → staging; staging, hotfix/* → main)
+- `check-commit-messages` - Enforces conventional commit format (skipped for staging → main)
+- `check-hotfix-version` - Ensures hotfix PRs include manual version bump
+- `run-tests` - Runs `npm test`
+
+**Secrets required:**
+| Secret | Purpose |
+|--------|---------|
+| `CHROME_EXTENSION_ID` | Chrome Web Store extension ID |
+| `CHROME_CLIENT_ID` | OAuth client ID |
+| `CHROME_CLIENT_SECRET` | OAuth client secret |
+| `CHROME_REFRESH_TOKEN` | OAuth refresh token |
+| `STAGING_PAT` | Personal Access Token for pushing to staging (bypasses branch protection) |
+
---
## Architecture Principles
@@ -325,9 +362,13 @@ npm test -- --watch # Watch mode
- **Re-runs use original commit code** - Push fix to new branch, don't just re-run
- **Release creation is idempotent** - Skips if tag already exists
- **Chrome Store review** - First public publish needs manual approval
-- **Version bumped on staging** - Not main (main is protected, staging isn't)
+- **Version bumped on staging** - Uses STAGING_PAT to bypass branch protection
- **Hotfixes need manual version bump** - CI checks that hotfix PRs bump the version
- **promote-staging calculates version** - From commits since last tag, highest bump wins
+- **Only extension/ changes trigger release** - Changes to README, tests, scripts alone won't publish
+- **`[skip ci]` hangs required checks** - Never use in commits that will be in PRs
+- **STAGING_PAT expires** - Fine-grained PAT needs rotation (check expiration date)
+- **Landing page changes use `(landing)` scope** - Won't trigger extension release
---
diff --git a/extension/manifest.json b/extension/manifest.json
index 3111362..b8a0aa8 100644
--- a/extension/manifest.json
+++ b/extension/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "PatternPulse - LeetCode Pattern Trainer",
- "version": "0.10.0",
+ "version": "0.182.0",
"description": "Train your pattern recognition! Quiz yourself on algorithmic patterns before seeing LeetCode hints, tags, and solutions.",
"icons": {
"16": "icons/icon-16.png",
diff --git a/landing_page/app/page.tsx b/landing_page/app/page.tsx
index 591c8f9..660f13e 100644
--- a/landing_page/app/page.tsx
+++ b/landing_page/app/page.tsx
@@ -1,44 +1,13 @@
'use client'
import { motion } from 'framer-motion'
-import { BarChart3, Chrome, Sparkles, Target, Brain, Mail, CheckCircle, Loader2 } from 'lucide-react'
+import { BarChart3, Chrome, Sparkles, Target, Brain, Shield, Zap } from 'lucide-react'
import { useState } from 'react'
-// TODO: Replace with your actual Chrome Web Store URL after publishing
-const CHROME_STORE_URL = 'https://chrome.google.com/webstore/detail/patternpulse/YOUR_EXTENSION_ID'
-
-// Google Form configuration
-const GOOGLE_FORM_ACTION = 'https://docs.google.com/forms/d/e/1FAIpQLScSMueGAnRqxrQuC9ziNgi72TlOM8uh-fSYVZS0J2SFw12q1A/formResponse'
-const GOOGLE_FORM_EMAIL_ENTRY = 'entry.1669615047'
+const CHROME_STORE_URL = 'https://chromewebstore.google.com/detail/patternpulse-leetcode-pat/mnfiladjdapefilfdimnombhfanpmepg'
export default function Home() {
const [openFaq, setOpenFaq] = useState
@@ -96,56 +65,36 @@ export default function Home() {
Stop memorizing solutions. Learn to identify the right approach before you start coding — just like in real interviews.
Something went wrong. Please try again.
- )} + {/* Primary CTA */} +- 100% free — Supports NeetCode 150 and LeetCode 75 with AI-powered hints -
+ {/* Trust signals */} +NeetCode 150, LeetCode 75, and the first 100 LeetCode problems. Continuously adding more.
+NeetCode 150, LeetCode 75, and more. New problems added weekly — working toward full LeetCode coverage.
- Get notified when PatternPulse launches. It's completely free. + Start training your pattern recognition today. It takes 10 seconds to install.
- {submitStatus === 'success' ? ( -- Works on any Chromium browser (Chrome, Edge, Brave, Arc) + Works on Chrome, Edge, Brave, Arc, and any Chromium browser