Skip to content

Commit 1fa2ea9

Browse files
committed
Merge branch 'v6'
2 parents 53957e8 + 8b9fa15 commit 1fa2ea9

91 files changed

Lines changed: 6941 additions & 1633 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 0 additions & 8 deletions
This file was deleted.

.github/workflows/pullpreview-multi-env.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,36 @@ on:
77

88
jobs:
99
deploy_env1:
10-
runs-on: ubuntu-latest
10+
runs-on: ubuntu-slim
1111
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')
1212
timeout-minutes: 30
1313
steps:
14-
- uses: actions/checkout@v4
14+
- uses: actions/checkout@v5
1515
- uses: "./"
1616
with:
1717
deployment_variant: env1
1818
label: pullpreview-multi-env
1919
admins: crohr,qbonnard
2020
app_path: ./examples/wordpress
21-
instance_type: micro_2_0
21+
instance_type: micro
2222
registries: docker://${{ secrets.GHCR_PAT }}@ghcr.io
2323
env:
2424
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
2525
AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"
2626

2727
deploy_env2:
28-
runs-on: ubuntu-latest
28+
runs-on: ubuntu-slim
2929
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')
3030
timeout-minutes: 30
3131
steps:
32-
- uses: actions/checkout@v4
32+
- uses: actions/checkout@v5
3333
- uses: "./"
3434
with:
3535
deployment_variant: env2
3636
label: pullpreview-multi-env
3737
admins: crohr,qbonnard
3838
app_path: ./examples/wordpress
39-
instance_type: micro_2_0
39+
instance_type: micro
4040
registries: docker://${{ secrets.GHCR_PAT }}@ghcr.io
4141
env:
4242
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"

.github/workflows/pullpreview.yml

Lines changed: 114 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,47 +5,138 @@ on:
55
push:
66
branches:
77
- master
8-
- v5
8+
- v6
99
pull_request:
10-
types: [labeled, unlabeled, synchronize, closed, reopened]
10+
types: [labeled, unlabeled, synchronize, closed, reopened, opened]
1111

1212
concurrency: ${{ github.ref }}
1313

1414
permissions:
1515
contents: read # to fetch code (actions/checkout)
16-
deployments: write # to delete deployments
17-
pull-requests: write # to remove labels
18-
statuses: write # to create commit status
16+
pull-requests: write # to remove labels / write PR comments
1917

2018
jobs:
21-
deploy:
22-
runs-on: ubuntu-latest
19+
deploy_smoke_1:
20+
runs-on: ubuntu-slim
2321
if: github.event_name == 'schedule' || github.event_name == 'push' || github.event.label.name == 'pullpreview' || contains(github.event.pull_request.labels.*.name, 'pullpreview')
24-
timeout-minutes: 30
22+
timeout-minutes: 35
2523
steps:
26-
- uses: actions/checkout@v4
27-
- uses: "./"
24+
- uses: actions/checkout@v6
25+
26+
- name: Deploy smoke app (v1)
27+
id: pullpreview
28+
uses: "./"
2829
with:
2930
admins: "@collaborators/push"
30-
always_on: master,v5
31-
app_path: ./examples/wordpress
31+
always_on: master,v6
32+
app_path: ./examples/workflow-smoke
3233
instance_type: micro
3334
# only required if using custom domain for your preview environments
34-
dns: custom.preview.run
35+
dns: preview.chunk.io
3536
max_domain_length: 30
36-
# only required if fetching images from private registry
37+
# Enable HTTPS preview URL through Caddy + Let's Encrypt.
38+
proxy_tls: web:8080
39+
# required here because the mysql image is private in GHCR
3740
registries: docker://${{ secrets.GHCR_PAT }}@ghcr.io
3841
# how long this instance will stay alive (each further commit will reset the timer)
3942
ttl: 1h
40-
# preinstall script to run on the instance before docker-compose is called, relative to the app_path
41-
pre_script: ./pre_script.sh
4243
env:
4344
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
4445
AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"
45-
# # Uncomment the following if need to debug
46-
# - name: Setup tmate session
47-
# if: always()
48-
# uses: mxschmitt/action-tmate@v3
49-
# timeout-minutes: 45
50-
# with:
51-
# limit-access-to-actor: true
46+
47+
- name: Assert deploy v1 and DB seed state
48+
shell: bash
49+
env:
50+
PREVIEW_URL: ${{ steps.pullpreview.outputs.url }}
51+
run: |
52+
set -euo pipefail
53+
54+
if [[ "${PREVIEW_URL}" != https://* ]]; then
55+
echo "::error::Expected https preview URL when proxy_tls is enabled, got ${PREVIEW_URL}"
56+
exit 1
57+
fi
58+
59+
response=""
60+
for attempt in $(seq 1 60); do
61+
response="$(curl -fsSL --max-time 15 "${PREVIEW_URL}" || true)"
62+
if printf '%s' "${response}" | grep -q 'Hello World Deploy 1' && \
63+
printf '%s' "${response}" | grep -q 'seed_count=1' && \
64+
printf '%s' "${response}" | grep -q 'seed_label=persisted'; then
65+
echo "Smoke v1 checks passed for ${PREVIEW_URL}"
66+
exit 0
67+
fi
68+
69+
echo "Attempt ${attempt}/60: waiting for v1 response from ${PREVIEW_URL}"
70+
sleep 5
71+
done
72+
73+
echo "::error::Unexpected response from ${PREVIEW_URL}"
74+
printf '%s\n' "${response}"
75+
exit 1
76+
77+
deploy_smoke_2:
78+
runs-on: ubuntu-slim
79+
needs: deploy_smoke_1
80+
if: needs.deploy_smoke_1.result == 'success'
81+
timeout-minutes: 35
82+
steps:
83+
- uses: actions/checkout@v6
84+
85+
- name: Update app payload to v2
86+
shell: bash
87+
run: |
88+
set -euo pipefail
89+
90+
printf '%s\n' 'Hello World Deploy 2' > examples/workflow-smoke/web/message.txt
91+
92+
# This file should be synced, but with persistent DB volume it should not run.
93+
cat > examples/workflow-smoke/dumps/999_should_not_run.sql <<'SQL'
94+
INSERT INTO seed_data (label) VALUES ('should-not-run');
95+
SQL
96+
97+
- name: Redeploy smoke app (v2)
98+
id: pullpreview
99+
uses: "./"
100+
with:
101+
admins: "@collaborators/push"
102+
always_on: master,v6
103+
app_path: ./examples/workflow-smoke
104+
instance_type: micro
105+
dns: preview.chunk.io
106+
max_domain_length: 30
107+
proxy_tls: web:8080
108+
registries: docker://${{ secrets.GHCR_PAT }}@ghcr.io
109+
ttl: 1h
110+
env:
111+
AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}"
112+
AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"
113+
114+
- name: Assert deploy v2 and DB persistence
115+
shell: bash
116+
env:
117+
PREVIEW_URL: ${{ steps.pullpreview.outputs.url }}
118+
run: |
119+
set -euo pipefail
120+
121+
if [[ "${PREVIEW_URL}" != https://* ]]; then
122+
echo "::error::Expected https preview URL when proxy_tls is enabled, got ${PREVIEW_URL}"
123+
exit 1
124+
fi
125+
126+
response=""
127+
for attempt in $(seq 1 60); do
128+
response="$(curl -fsSL --max-time 15 "${PREVIEW_URL}" || true)"
129+
if printf '%s' "${response}" | grep -q 'Hello World Deploy 2' && \
130+
printf '%s' "${response}" | grep -q 'seed_count=1' && \
131+
printf '%s' "${response}" | grep -q 'seed_label=persisted'; then
132+
echo "Smoke v2 checks passed for ${PREVIEW_URL}"
133+
exit 0
134+
fi
135+
136+
echo "Attempt ${attempt}/60: waiting for v2 response from ${PREVIEW_URL}"
137+
sleep 5
138+
done
139+
140+
echo "::error::Unexpected response from ${PREVIEW_URL}"
141+
printf '%s\n' "${response}"
142+
exit 1

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.swp
22
.env*
33
tempkey
4+
.DS_Store

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "wiki"]
2+
path = wiki
3+
url = https://github.com/pullpreview/action.wiki.git

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ruby 3.1.6
1+
go 1.25.1

AGENTS.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# PullPreview Action — Current Behavior (Go)
2+
3+
This repository ships a GitHub Action implemented in Go.
4+
5+
## Runtime
6+
- Action definition: `action.yml`
7+
- Action type: `composite`
8+
- Runtime binary: prebuilt amd64 Linux artifact in `dist/`
9+
- No Docker image build is required during action execution.
10+
11+
## Go Tooling
12+
- Go commands should be run via `mise` for toolchain consistency.
13+
- Examples:
14+
- `mise exec -- go test ./...`
15+
- `mise exec -- go run ./cmd/pullpreview up examples/example-app`
16+
- `make dist`
17+
- Dist workflow:
18+
- Commit source changes first.
19+
- Run `make dist` afterwards.
20+
- `make dist` auto-commits the updated bundled binary with a standard commit message.
21+
- Before merging, `make rewrite` can rewrite the current branch and drop dist-only auto-commits (force-push required).
22+
23+
## CLI
24+
Entrypoint source is `cmd/pullpreview/main.go`.
25+
26+
Supported commands:
27+
- `pullpreview up path/to/app`
28+
- `pullpreview down --name <instance>`
29+
- `pullpreview list org/repo`
30+
- `pullpreview github-sync path/to/app`
31+
32+
## Deploy behavior (`up`)
33+
- Launches/restores Lightsail instance and waits for SSH.
34+
- Uploads authorized keys.
35+
- Renders compose config, rewrites relative bind mounts under `app_path` to `/app/...`, and syncs only those bind-mounted local paths to the server via `rsync`.
36+
- Deploys through Docker context to the remote engine.
37+
- Executes `pre_script` inline over SSH before `docker compose up` (script must be self-contained).
38+
- Optional automatic HTTPS proxying via Caddy + Let's Encrypt when `proxy_tls` is set.
39+
- Format: `service:port` (for example `web:80`).
40+
- Forces preview URL/output to HTTPS on port `443`.
41+
- Opens firewall port `443` and suppresses firewall exposure for port `80`.
42+
- Injects `pullpreview-proxy` service unless host port `443` is already published (then it logs a warning and skips proxy injection).
43+
- Emits periodic heartbeat logs with:
44+
- preview URL
45+
- SSH command (`ssh user@ip`)
46+
- authorized users info
47+
- key-upload confirmation
48+
49+
## GitHub sync behavior (`github-sync`)
50+
- Handles PR labeled/opened/reopened/synchronize/unlabeled/closed events.
51+
- Handles push events for `always_on` branches.
52+
- Handles scheduled cleanup of dangling labeled preview instances.
53+
- Updates marker-based PR status comments.
54+
- For `admins: "@collaborators/push"`:
55+
- loads collaborators from GitHub REST API with `affiliation=all` + `permission=push`
56+
- uses only the first page (up to 100 users)
57+
- emits a warning if additional pages exist
58+
- fetches each admin's SSH public keys via GitHub API and forwards keys to the instance
59+
- uses local key cache directory (`PULLPREVIEW_SSH_KEYS_CACHE_DIR`) to avoid refetching keys across runs
60+
- Always posts/updates marker-based PR status comments per environment/job with building/ready/error/destroyed state and preview URL.
61+
62+
## Action inputs/outputs
63+
- Existing inputs are preserved.
64+
- Additional input:
65+
- `proxy_tls` (`service:port`, default empty)
66+
- Outputs:
67+
- `url`
68+
- `host`
69+
- `username`
70+
71+
## Key directories
72+
- `cmd/pullpreview`: CLI
73+
- `internal/pullpreview`: core orchestration
74+
- `internal/providers/lightsail`: Lightsail provider
75+
- `internal/github`: GitHub API wrapper
76+
- `internal/license`: license check client
77+
- `dist/`: bundled Linux amd64 binary used by the action
78+
79+
## Repo-local skill
80+
- `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.

CHANGELOG.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,29 @@
1-
## master
1+
## v6.0.0
2+
3+
### Breaking changes
4+
5+
- **Complete rewrite from Ruby/Docker to Go** — PullPreview now ships as a native binary in a composite action. No more Docker container overhead.
6+
- **Removed GitHub Deployments/Environments integration** — preview URLs are now displayed via PR comments and job summaries.
7+
- **Removed commit status integration** — deployment state is reported through PR comments only.
8+
- **Removed `comment_pr` input** — PR comment updates are always enabled.
9+
- Workflow references should now use `pullpreview/action@v6`.
10+
11+
### New features
12+
13+
- **Automatic HTTPS with `proxy_tls`** — inject a Caddy reverse proxy with Let's Encrypt certificates (e.g. `proxy_tls: web:80`).
14+
- **Built-in DNS alternatives** — use `rev1.click` through `rev9.click` to work around Let's Encrypt rate limits (50 certs/domain/week).
15+
- **`pre_script` input** — run a local shell script on the instance before docker compose.
16+
- **`ttl` input** — set a maximum deployment lifetime (e.g. `10h`, `5d`, `infinite`).
17+
- **Job summary** — deployment details are added to the GitHub Actions job summary.
18+
- **SSH key caching** — collaborator SSH keys are cached between workflow runs via the action cache.
19+
- **Compose troubleshooting** — docker container health status is displayed on deploy failures.
20+
- **Scheduled cleanup reconciliation** — dangling instances are reconciled against repo state during scheduled runs.
21+
22+
### Improvements
23+
24+
- Deploy files via rsync with bind mounts instead of Docker context.
25+
- PR comments are scoped by environment and job for multi-env setups.
26+
- PullPreview environment variables are injected during compose interpolation.
227

328
## v5.8.0
429

Dockerfile

Lines changed: 0 additions & 12 deletions
This file was deleted.

Gemfile

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)