Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4f8443f
Migrate action runtime to Go with tests and live deploy path
crohr Feb 6, 2026
81a060c
Trigger PR synchronize workflow
crohr Feb 6, 2026
2844126
Trigger workflow after re-enabling pullpreview
crohr Feb 6, 2026
3bc9583
Fix path-first flag parsing for action compatibility
crohr Feb 6, 2026
c4cce5a
Add heartbeat logs, PR comments, and bundled binary action runtime
crohr Feb 6, 2026
7166598
ubuntu-slim
crohr Feb 6, 2026
114c54a
Remove obsolete Ruby and Docker runtime artifacts
crohr Feb 6, 2026
204656d
POC: deploy via Docker context with bind mount rewrite
crohr Feb 6, 2026
bd10d2a
Use Docker/Compose Go SDK and fix context registry auth
crohr Feb 6, 2026
58d9e83
chore: enforce dist workflow and amd64-only runtime
crohr Feb 6, 2026
0ecd8e7
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
23167e1
shrink: remove compose sdk dependency closure
crohr Feb 6, 2026
ac9244a
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
283b3de
test with custom domain
crohr Feb 6, 2026
89ce1e4
ci: add two-stage pullpreview smoke workflow
crohr Feb 6, 2026
95ee049
feat: add proxy_tls https forwarding support
crohr Feb 6, 2026
ae7de53
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
edc72e3
fix
crohr Feb 6, 2026
8c53876
fix: address Copilot PR review comments
crohr Feb 6, 2026
2fa481e
feat: propagate context and cache collaborator ssh keys
crohr Feb 6, 2026
4bc443a
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
b397f66
fix: update PR comment on preview destroy
crohr Feb 6, 2026
696cdf8
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
4c37221
feat: add job summary and pullpreview-linked PR title
crohr Feb 6, 2026
3942456
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
560e0f2
docs: refresh wiki and add demo flow screenshots
crohr Feb 6, 2026
de6d03e
fix
crohr Feb 6, 2026
54bfb78
fix: scope PR comments by env/job and add demo flow skill
crohr Feb 6, 2026
70af287
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
0f42a86
breaking: remove github deployments integration and comment_pr
crohr Feb 6, 2026
2d3f75d
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
62c36ec
breaking: drop commit status integration
crohr Feb 6, 2026
3eead75
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
2417f02
feat: link logs to job URL and add rewrite target
crohr Feb 6, 2026
e08f4a5
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
c362524
chore: upgrade deps and pin go 1.25
crohr Feb 6, 2026
56156d3
chore(dist): update bundled pullpreview binary
crohr Feb 6, 2026
1b26c26
Remove
crohr Feb 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .dockerignore

This file was deleted.

12 changes: 6 additions & 6 deletions .github/workflows/pullpreview-multi-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,36 @@ on:

jobs:
deploy_env1:
runs-on: ubuntu-latest
runs-on: ubuntu-slim
if: github.event_name == 'schedule' || github.event_name == 'push' || github.event.label.name == 'pullpreview-multi-env' || contains(github.event.pull_request.labels.*.name, 'pullpreview-multi-env')
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: "./"
with:
deployment_variant: env1
label: pullpreview-multi-env
admins: crohr,qbonnard
app_path: ./examples/wordpress
instance_type: micro_2_0
instance_type: micro
registries: docker://${{ secrets.GHCR_PAT }}@ghcr.io
env:
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"

deploy_env2:
runs-on: ubuntu-latest
runs-on: ubuntu-slim
if: github.event_name == 'schedule' || github.event_name == 'push' || github.event.label.name == 'pullpreview-multi-env' || contains(github.event.pull_request.labels.*.name, 'pullpreview-multi-env')
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: "./"
with:
deployment_variant: env2
label: pullpreview-multi-env
admins: crohr,qbonnard
app_path: ./examples/wordpress
instance_type: micro_2_0
instance_type: micro
registries: docker://${{ secrets.GHCR_PAT }}@ghcr.io
env:
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
Expand Down
149 changes: 134 additions & 15 deletions .github/workflows/pullpreview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,157 @@ on:
- master
- v5
pull_request:
types: [labeled, unlabeled, synchronize, closed, reopened]
types: [labeled, unlabeled, synchronize, closed, reopened, opened]

concurrency: ${{ github.ref }}

permissions:
contents: read # to fetch code (actions/checkout)
deployments: write # to delete deployments
pull-requests: write # to remove labels
statuses: write # to create commit status
pull-requests: write # to remove labels / write PR comments

jobs:
deploy:
runs-on: ubuntu-latest
if: github.event_name == 'schedule' || github.event_name == 'push' || github.event.label.name == 'pullpreview' || contains(github.event.pull_request.labels.*.name, 'pullpreview')
timeout-minutes: 30
deploy_smoke_1:
runs-on: ubuntu-slim
if: github.event_name == 'push' || (github.event.action != 'closed' && github.event.action != 'unlabeled' && (github.event.label.name == 'pullpreview' || contains(github.event.pull_request.labels.*.name, 'pullpreview')))
timeout-minutes: 35
steps:
- uses: actions/checkout@v4
- uses: "./"
- uses: actions/checkout@v5

- name: Deploy smoke app (v1)
id: pullpreview
uses: "./"
with:
admins: "@collaborators/push"
always_on: master,v5
app_path: ./examples/wordpress
app_path: ./examples/workflow-smoke
instance_type: micro
# only required if using custom domain for your preview environments
dns: custom.preview.run
dns: preview.chunk.io
max_domain_length: 30
# only required if fetching images from private registry
# Enable HTTPS preview URL through Caddy + Let's Encrypt.
proxy_tls: web:8080
# required here because the mysql image is private in GHCR
registries: docker://${{ secrets.GHCR_PAT }}@ghcr.io
# how long this instance will stay alive (each further commit will reset the timer)
ttl: 1h
# preinstall script to run on the instance before docker-compose is called, relative to the app_path
pre_script: ./pre_script.sh
env:
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"

- name: Assert deploy v1 and DB seed state
shell: bash
env:
PREVIEW_URL: ${{ steps.pullpreview.outputs.url }}
run: |
set -euo pipefail

if [[ "${PREVIEW_URL}" != https://* ]]; then
echo "::error::Expected https preview URL when proxy_tls is enabled, got ${PREVIEW_URL}"
exit 1
fi

response=""
for attempt in $(seq 1 60); do
response="$(curl -fsSL --max-time 15 "${PREVIEW_URL}" || true)"
if printf '%s' "${response}" | grep -q 'Hello World Deploy 1' && \
printf '%s' "${response}" | grep -q 'seed_count=1' && \
printf '%s' "${response}" | grep -q 'seed_label=persisted'; then
echo "Smoke v1 checks passed for ${PREVIEW_URL}"
exit 0
fi

echo "Attempt ${attempt}/60: waiting for v1 response from ${PREVIEW_URL}"
sleep 5
done

echo "::error::Unexpected response from ${PREVIEW_URL}"
printf '%s\n' "${response}"
exit 1

deploy_smoke_2:
runs-on: ubuntu-slim
needs: deploy_smoke_1
if: needs.deploy_smoke_1.result == 'success'
timeout-minutes: 35
steps:
- uses: actions/checkout@v5

- name: Update app payload to v2
shell: bash
run: |
set -euo pipefail

printf '%s\n' 'Hello World Deploy 2' > examples/workflow-smoke/web/message.txt

# This file should be synced, but with persistent DB volume it should not run.
cat > examples/workflow-smoke/dumps/999_should_not_run.sql <<'SQL'
INSERT INTO seed_data (label) VALUES ('should-not-run');
SQL

- name: Redeploy smoke app (v2)
id: pullpreview
uses: "./"
with:
admins: "@collaborators/push"
always_on: master,v5
app_path: ./examples/workflow-smoke
instance_type: micro
dns: preview.chunk.io
max_domain_length: 30
proxy_tls: web:8080
registries: docker://${{ secrets.GHCR_PAT }}@ghcr.io
ttl: 1h
env:
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"

- name: Assert deploy v2 and DB persistence
shell: bash
env:
PREVIEW_URL: ${{ steps.pullpreview.outputs.url }}
run: |
set -euo pipefail

if [[ "${PREVIEW_URL}" != https://* ]]; then
echo "::error::Expected https preview URL when proxy_tls is enabled, got ${PREVIEW_URL}"
exit 1
fi

response=""
for attempt in $(seq 1 60); do
response="$(curl -fsSL --max-time 15 "${PREVIEW_URL}" || true)"
if printf '%s' "${response}" | grep -q 'Hello World Deploy 2' && \
printf '%s' "${response}" | grep -q 'seed_count=1' && \
printf '%s' "${response}" | grep -q 'seed_label=persisted'; then
echo "Smoke v2 checks passed for ${PREVIEW_URL}"
exit 0
fi

echo "Attempt ${attempt}/60: waiting for v2 response from ${PREVIEW_URL}"
sleep 5
done

echo "::error::Unexpected response from ${PREVIEW_URL}"
printf '%s\n' "${response}"
exit 1

cleanup:
runs-on: ubuntu-slim
if: github.event_name == 'schedule' || github.event.action == 'closed' || (github.event.action == 'unlabeled' && github.event.label.name == 'pullpreview')
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: "./"
with:
admins: "@collaborators/push"
always_on: master,v5
app_path: ./examples/workflow-smoke
instance_type: micro
dns: toto.preview.run
max_domain_length: 30
proxy_tls: web:8080
registries: docker://${{ secrets.GHCR_PAT }}@ghcr.io
ttl: 1h
env:
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "wiki"]
path = wiki
url = https://github.com/pullpreview/action.wiki.git
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ruby 3.1.6
go 1.25.1
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .tool-versions specifies Go version "1.25.1", but go.mod declares go 1.23. There is no Go 1.25 release yet (the latest stable as of January 2025 is 1.23). This appears to be a typo - either use "1.23.1" in .tool-versions or update both to match a valid Go version.

Suggested change
go 1.25.1
go 1.23.1

Copilot uses AI. Check for mistakes.
80 changes: 80 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# PullPreview Action — Current Behavior (Go)

This repository ships a GitHub Action implemented in Go.

## Runtime
- Action definition: `action.yml`
- Action type: `composite`
- Runtime binary: prebuilt amd64 Linux artifact in `dist/`
- No Docker image build is required during action execution.

## Go Tooling
- Go commands should be run via `mise` for toolchain consistency.
- Examples:
- `mise exec -- go test ./...`
- `mise exec -- go run ./cmd/pullpreview up examples/example-app`
- `make dist`
- Dist workflow:
- Commit source changes first.
- Run `make dist` afterwards.
- `make dist` auto-commits the updated bundled binary with a standard commit message.
- Before merging, `make rewrite` can rewrite the current branch and drop dist-only auto-commits (force-push required).

## CLI
Entrypoint source is `cmd/pullpreview/main.go`.

Supported commands:
- `pullpreview up path/to/app`
- `pullpreview down --name <instance>`
- `pullpreview list org/repo`
- `pullpreview github-sync path/to/app`

## Deploy behavior (`up`)
- Tars app directory (excluding `.git`).
- Launches/restores Lightsail instance and waits for SSH.
- Uploads authorized keys, update script, and pre-script.
- Deploys through Docker context to the remote engine.
- Rewrites relative bind mounts under `app_path` so they resolve on the remote host.
- Optional automatic HTTPS proxying via Caddy + Let's Encrypt when `proxy_tls` is set.
- Format: `service:port` (for example `web:80`).
- Forces preview URL/output to HTTPS on port `443`.
- Opens firewall port `443` and suppresses firewall exposure for port `80`.
- Injects `pullpreview-proxy` service unless host port `443` is already published (then it logs a warning and skips proxy injection).
- Emits periodic heartbeat logs with:
- preview URL
- SSH command (`ssh user@ip`)
- authorized users info
- key-upload confirmation

## GitHub sync behavior (`github-sync`)
- Handles PR labeled/opened/reopened/synchronize/unlabeled/closed events.
- Handles push events for `always_on` branches.
- Handles scheduled cleanup of dangling labeled preview instances.
- Updates marker-based PR status comments.
- For `admins: "@collaborators/push"`:
- loads collaborators from GitHub REST API with `affiliation=all` + `permission=push`
- uses only the first page (up to 100 users)
- emits a warning if additional pages exist
- fetches each admin's SSH public keys via GitHub API and forwards keys to the instance
- uses local key cache directory (`PULLPREVIEW_SSH_KEYS_CACHE_DIR`) to avoid refetching keys across runs
- Always posts/updates marker-based PR status comments per environment/job with building/ready/error/destroyed state and preview URL.

## Action inputs/outputs
- Existing inputs are preserved.
- Additional input:
- `proxy_tls` (`service:port`, default empty)
- Outputs:
- `url`
- `host`
- `username`

## Key directories
- `cmd/pullpreview`: CLI
- `internal/pullpreview`: core orchestration
- `internal/providers/lightsail`: Lightsail provider
- `internal/github`: GitHub API wrapper
- `internal/license`: license check client
- `dist/`: bundled Linux amd64 binary used by the action

## Repo-local skill
- `skills/pullpreview-demo-flow/SKILL.md`: repeatable end-to-end demo capture workflow (PR open/label/deploy/view deployment/unlabel/destroy) with strict screenshot requirements and fixed demo PR title.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## master

### Breaking changes

- Removed GitHub Deployments/Environments integration from the Go action runtime.
- PullPreview now relies on workflow checks + PR comments as the deployment UX surface.
- Removed `comment_pr` input and `--comment-pr` CLI flag; PR comment updates are always enabled.

## v5.8.0

- Switch default domain to my.preview.run (#92)
Expand Down
12 changes: 0 additions & 12 deletions Dockerfile

This file was deleted.

7 changes: 0 additions & 7 deletions Gemfile

This file was deleted.

Loading