Skip to content

Commit fc6df5f

Browse files
riderxclaude
andauthored
Add blog: Turn Every PR Into an Installable Preview (#444)
* Add blog article: Turn Every Pull Request Into an Installable Preview Covers how to set up PR previews with Capgo using GitHub Actions, enabling QA, PMs, and developers to test features without TestFlight. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: update created_at date to 2026 Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: address PR review comments - Use existing head_image (capgo_ci-cd-illustration.webp) - Only post PR comment on 'opened' event (not synchronize) - Add continue-on-error for channel creation (may exist) - Show complete Capacitor config structure - Use @capgo/cli@latest explicitly in workflows Co-Authored-By: Claude Opus 4.5 <[email protected]> * docs: expand custom UI section with listChannels and setChannel Show complete flow for building custom channel selector: - listChannels() to discover available PR channels - setChannel() to switch to selected channel - getChannel() to show current channel - unsetChannel() to return to production Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: correct shake menu config and add detail - Add allowShakeChannelSelector: true to config - Update description to explain full shake menu flow - Document automatic update behavior after channel selection Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 4305044 commit fc6df5f

File tree

1 file changed

+306
-0
lines changed

1 file changed

+306
-0
lines changed
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
---
2+
slug: turn-every-pr-into-installable-preview
3+
title: "Turn Every Pull Request Into an Installable Preview"
4+
description: "Stop waiting for TestFlight processing. Capgo PR previews let QA, PMs, and stakeholders test features on real devices in under a minute."
5+
author: Martin Donadieu
6+
author_image_url: 'https://avatars.githubusercontent.com/u/4084527?v=4'
7+
author_url: 'https://x.com/martindonadieu'
8+
created_at: 2026-01-22T00:00:00.000Z
9+
updated_at: 2026-01-22T00:00:00.000Z
10+
head_image: /capgo_ci-cd-illustration.webp
11+
head_image_alt: Capgo PR preview illustration
12+
keywords: pr preview, pull request, OTA updates, capacitor, capgo, live updates, QA testing, feature preview, github actions
13+
tag: Tutorial
14+
published: true
15+
locale: en
16+
next_blog: ''
17+
---
18+
19+
Every mobile dev team has felt the pain: a feature is ready for review, but getting it into stakeholders' hands means navigating the TestFlight or Google Play beta review maze. What should take minutes turns into hours of waiting, installing, and managing beta builds.
20+
21+
What if your production app could pull the latest changes from any pull request directly onto the device, without any reinstalls or app store delays?
22+
23+
That's what **PR previews** enable. When a developer opens a pull request, a GitHub Action creates a dedicated update channel and publishes the changes. Anyone with the app installed can switch to that channel, test the feature, and switch back - all without leaving the app they already have.
24+
25+
## The TestFlight Problem
26+
27+
The traditional workflow for testing mobile features looks something like this:
28+
29+
1. **Developer opens PR** - Code is ready for review
30+
2. **Wait for TestFlight** - 15-30 minutes of processing time
31+
3. **Find and install** - Testers search for the right build
32+
4. **Test and repeat** - Every change means another wait
33+
34+
This creates a bottleneck. QA gets blocked waiting for builds. Product managers can't verify features quickly. Developers lose context while waiting for feedback. The industry estimates this costs around $340 per PR in lost productivity.
35+
36+
## How PR Previews Work
37+
38+
PR previews use Capgo's channel system to create per-PR update streams. Here's the flow:
39+
40+
1. **PR opened or updated** - GitHub Action triggers
41+
2. **Bundle uploaded** - Your JS/CSS changes go to a PR-specific channel
42+
3. **Comment posted** - Testers get instructions in the PR
43+
4. **Instant testing** - Switch channels, test, switch back
44+
45+
No new app installations. No TestFlight delays. The same production app can pull from different update channels.
46+
47+
## Setting Up PR Previews
48+
49+
Before you can implement PR previews, your project needs to be configured with Capgo Live Updates. Follow the [Capgo quickstart guide](/docs/getting-started/quickstart/) if you haven't already.
50+
51+
### GitHub Actions Workflow
52+
53+
Create `.github/workflows/pr-preview.yml`:
54+
55+
```yaml
56+
name: PR Preview
57+
on:
58+
pull_request:
59+
types: [opened, synchronize]
60+
61+
jobs:
62+
deploy:
63+
runs-on: ubuntu-latest
64+
steps:
65+
- uses: actions/checkout@v4
66+
67+
- name: Setup Bun
68+
uses: oven-sh/setup-bun@v1
69+
70+
- name: Install Dependencies
71+
run: bun install
72+
73+
- name: Build
74+
run: bun run build
75+
76+
# Create a channel named after your PR (may already exist on synchronize)
77+
- name: Create PR Channel
78+
id: create_channel
79+
continue-on-error: true
80+
run: bunx @capgo/cli@latest channel add pr-${{ github.event.pull_request.number }} --self-assign
81+
env:
82+
CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
83+
84+
# Upload the build to that channel
85+
- name: Upload to Capgo
86+
run: bunx @capgo/cli@latest bundle upload --channel pr-${{ github.event.pull_request.number }}
87+
env:
88+
CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
89+
90+
# Post a comment with testing instructions (only on PR open)
91+
- name: Comment on PR
92+
if: github.event.action == 'opened'
93+
uses: actions/github-script@v7
94+
with:
95+
script: |
96+
github.rest.issues.createComment({
97+
owner: context.repo.owner,
98+
repo: context.repo.repo,
99+
issue_number: ${{ github.event.pull_request.number }},
100+
body: '📱 **Test this PR on device:**\n\nOpen your app and switch to channel: `pr-${{ github.event.pull_request.number }}`\n\nUse the shake menu or call `setChannel()` from your app.'
101+
})
102+
```
103+
104+
The key is the `--self-assign` flag when creating the channel. This enables testers to switch to the channel from within the app using the `setChannel()` API.
105+
106+
### Setting Up Capgo Token
107+
108+
1. Go to your [Capgo dashboard](https://web.capgo.app)
109+
2. Navigate to Settings > API Keys
110+
3. Generate a new key with `all` permissions
111+
4. Add it as `CAPGO_TOKEN` in your GitHub repository secrets
112+
113+
## How Testers Switch Channels
114+
115+
There are two ways for testers to switch to a PR channel:
116+
117+
### Option 1: Shake Menu (Simplest)
118+
119+
Enable the shake menu with channel selector in your Capacitor config:
120+
121+
```typescript
122+
// capacitor.config.ts
123+
const config: CapacitorConfig = {
124+
// ... your other config
125+
plugins: {
126+
CapacitorUpdater: {
127+
shakeMenu: true,
128+
allowShakeChannelSelector: true
129+
}
130+
}
131+
};
132+
```
133+
134+
Testers shake their device to open the debug menu, which shows a list of available channels with a search bar. They find their PR channel (e.g., `pr-123`), tap to select it, and the app automatically downloads and applies the update. When done testing, they shake again and switch back to production.
135+
136+
The shake menu handles the entire flow automatically:
137+
1. Fetches all self-assignable channels via `listChannels()`
138+
2. Displays channels with search to find specific PRs
139+
3. Downloads the update after selection
140+
4. Prompts to reload with "Reload Now" / "Later" options
141+
142+
### Option 2: Custom Channel Selector UI
143+
144+
Build a channel switcher into your app that lists available PR channels and lets testers pick one. This uses two key APIs:
145+
146+
- `listChannels()` - Fetches all channels with self-assignment enabled
147+
- `setChannel()` - Switches the device to the selected channel
148+
149+
```typescript
150+
import { CapacitorUpdater } from '@capgo/capacitor-updater';
151+
152+
// Get all available channels (including PR channels)
153+
async function getAvailableChannels() {
154+
const { channels } = await CapacitorUpdater.listChannels();
155+
156+
// Filter to show only PR channels
157+
const prChannels = channels.filter(c => c.name.startsWith('pr-'));
158+
159+
return prChannels;
160+
}
161+
162+
// Switch to a specific PR channel
163+
async function switchToChannel(channelName: string) {
164+
await CapacitorUpdater.setChannel({
165+
channel: channelName,
166+
triggerAutoUpdate: true // Immediately check for updates
167+
});
168+
}
169+
170+
// Return to production
171+
async function switchBackToProduction() {
172+
await CapacitorUpdater.unsetChannel({});
173+
}
174+
175+
// Get current channel
176+
async function getCurrentChannel() {
177+
const { channel } = await CapacitorUpdater.getChannel();
178+
return channel;
179+
}
180+
```
181+
182+
With these building blocks, you can create a simple UI:
183+
184+
```typescript
185+
// Example: List PR channels and let user select
186+
const channels = await getAvailableChannels();
187+
const current = await getCurrentChannel();
188+
189+
// Display channels in your UI
190+
channels.forEach(channel => {
191+
console.log(`${channel.name} ${channel.name === current ? '(current)' : ''}`);
192+
});
193+
194+
// When user selects a channel
195+
await switchToChannel('pr-123');
196+
```
197+
198+
For a complete React component example, see [our channel surfing article](/blog/how-capgo-channel-switching-works/).
199+
200+
## Cleaning Up PR Channels
201+
202+
When a PR is merged or closed, you'll want to clean up the channel. Add another workflow:
203+
204+
```yaml
205+
name: Cleanup PR Preview
206+
on:
207+
pull_request:
208+
types: [closed]
209+
210+
jobs:
211+
cleanup:
212+
runs-on: ubuntu-latest
213+
steps:
214+
- name: Delete PR Channel
215+
run: bunx @capgo/cli@latest channel delete pr-${{ github.event.pull_request.number }}
216+
env:
217+
CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
218+
```
219+
220+
This removes the channel when the PR is closed, keeping your channel list clean.
221+
222+
## Version Compatibility
223+
224+
PR previews only work when the JavaScript bundle is compatible with the installed native version. If your PR includes native code changes (new Capacitor plugins, iOS/Android modifications), testers will need a new native build.
225+
226+
Capgo automatically checks version compatibility. If a PR's bundle targets a different native version than what's installed, the update won't be applied. This prevents crashes from incompatible code.
227+
228+
For PRs that do require native changes, you'll need to distribute a new TestFlight/Play Store build. PR previews work best for JavaScript, CSS, and asset changes that don't touch native code.
229+
230+
## Who Benefits from PR Previews
231+
232+
### QA Engineers
233+
234+
- Test features immediately when PRs are opened
235+
- Switch between multiple PRs without reinstalling
236+
- Verify fixes and regressions on real devices
237+
- No more waiting for TestFlight processing
238+
239+
### Product Managers
240+
241+
- Review features before they're merged
242+
- Give feedback directly on the PR
243+
- Verify that implementation matches requirements
244+
- Reduce review cycle time
245+
246+
### Developers
247+
248+
- Get faster feedback on changes
249+
- Demo features to stakeholders instantly
250+
- Debug issues with specific users
251+
- Spend less time managing beta builds
252+
253+
## Comparison: Traditional vs PR Previews
254+
255+
| Aspect | TestFlight/Beta | Capgo PR Preview |
256+
|--------|----------------|------------------|
257+
| Build time | 15-30 min | <1 min |
258+
| Switching PRs | 5+ min reinstall | 10 seconds |
259+
| Setup complexity | App Store credentials | One workflow file |
260+
| Cleanup | Manual | Automatic |
261+
| Native code changes | Required | Optional (JS only) |
262+
263+
## Best Practices
264+
265+
1. **Name channels clearly**: Use `pr-{number}` convention for easy identification
266+
2. **Auto-cleanup**: Always delete channels when PRs close
267+
3. **Limit access**: Only enable shake menu in debug/staging builds
268+
4. **Document the process**: Add testing instructions to your PR template
269+
5. **Handle failures gracefully**: Check that channel creation succeeds before posting comments
270+
271+
## When Not to Use PR Previews
272+
273+
PR previews are for JavaScript/CSS changes. If your PR includes:
274+
275+
- New Capacitor plugins
276+
- iOS native code changes
277+
- Android native code changes
278+
- Dependency updates that affect native builds
279+
280+
You'll need traditional TestFlight/Play Store distribution for those changes.
281+
282+
## Combining with Channel Surfing
283+
284+
PR previews work best when combined with [channel surfing](/blog/how-capgo-channel-switching-works/). Your app can have:
285+
286+
- `production` - Stable releases for all users
287+
- `beta` - Early access for opt-in users
288+
- `pr-123` - Feature previews for specific PRs
289+
290+
Testers with production builds can switch to any PR channel, test the feature, then switch back - all with the same installed app.
291+
292+
## Resources
293+
294+
- [Capgo Live Updates Documentation](/docs/getting-started/quickstart/)
295+
- [Channels Documentation](/docs/live-updates/channels/)
296+
- [Channel Surfing Guide](/blog/how-capgo-channel-switching-works/)
297+
- [CLI Commands Reference](/docs/cli/commands/)
298+
- [PR Preview Solutions Page](/solutions/pr-preview/)
299+
300+
## Conclusion
301+
302+
PR previews transform how your team reviews and tests mobile features. Instead of waiting for TestFlight processing and managing multiple beta builds, testers can switch to any PR channel in seconds using the app they already have installed.
303+
304+
The setup is minimal - one GitHub Actions workflow file - and the benefits compound across your team. QA stays unblocked, product managers review faster, and developers get quicker feedback.
305+
306+
Start by adding the workflow to one repository and see how it changes your review process.

0 commit comments

Comments
 (0)