Demo videos from PR builds now expire after 90 days and are posted
directly to PR threads. Uses object tagging with S3 lifecycle policies
for automatic cleanup.
## Changes
**Workflow** (`.github/workflows/tests.yml`)
- Replace `hkusu/s3-upload-action@v2` with
`ramonpaolo/action-upload-s3@main` for tagging support
- Tag uploaded objects: `expiration=90days`, `Source=github-actions`,
`Type=pr-demo-video`
- Generate unique filenames: `pr-{number}-{timestamp}.gif`
- Post PR comment with embedded video using `actions/github-script@v7`
**Documentation** (`CI_CD.md`)
- S3 lifecycle policy configuration (filters on `expiration=90days` tag)
- IAM permission requirements: `s3:PutObject`, `s3:PutObjectTagging`
## S3 Lifecycle Setup Required
```json
{
"Rules": [{
"ID": "ExpirePRDemoVideosAfter90Days",
"Status": "Enabled",
"Filter": {"Tag": {"Key": "expiration", "Value": "90days"}},
"Expiration": {"Days": 90}
}]
}
```
Apply with: `aws s3api put-bucket-lifecycle-configuration --bucket
<bucket> --lifecycle-configuration file://policy.json`
## Notes
- gh-pages `video.mp4` unaffected (served from GitHub Pages, not S3)
- Existing S3 objects without tags remain unchanged
> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `https://api.github.com/repos/ramonpaolo/action-upload-s3/tags`
> - Triggering command: `/usr/bin/curl curl -s REDACTED` (http block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/thomasnordquist/MQTT-Explorer/settings/copilot/coding_agent)
(admins only)
>
> </details>
<!-- START COPILOT CODING AGENT SUFFIX -->
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
> when a demo-video is generated from a pr, add an expiration of 90 days
to the S3 file and post the video as image to the pr thread.
</details>
<!-- START COPILOT CODING AGENT TIPS -->
---
✨ Let Copilot coding agent [set things up for
you](https://github.com/thomasnordquist/MQTT-Explorer/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: thomasnordquist <7721625+thomasnordquist@users.noreply.github.com>
8.2 KiB
CI/CD Pipeline Documentation
Overview
MQTT Explorer uses GitHub Actions for continuous integration and testing. The pipeline tests both Electron (desktop) and browser modes.
Workflows
Test Workflow (.github/workflows/tests.yml)
This workflow runs on pull requests to master, beta, and release branches.
Docker Browser Build Workflow (.github/workflows/docker-browser.yml)
This workflow builds and publishes a Docker image for the browser mode.
Triggers:
- Push to
master,beta, orreleasebranches (when relevant files change) - Schedule: Runs every two weeks (1st and 15th of each month at 2:00 AM UTC)
- Manual trigger via workflow_dispatch
Platforms:
- linux/amd64 (x86-64)
- linux/arm64 (Raspberry Pi 3/4/5, Apple Silicon)
- linux/arm/v7 (Raspberry Pi 2/3)
Image Registry: GitHub Container Registry (ghcr.io/thomasnordquist/mqtt-explorer)
Tags:
latest- Latest build from master branchmaster- Latest build from masterbeta- Latest build from beta branchrelease- Latest build from release branch<branch>-<sha>- Specific commit builds
Steps:
- Build Docker image with multi-stage build
- Test basic startup with test credentials
- Test health check
- Verify HTTP response
- Test data directory creation
- Check Docker image size
- Start container for frontend tests
- Test frontend bundles (app.bundle.js, vendors.bundle.js)
- Push image to GitHub Container Registry
- Generate build attestation for supply chain security
Image Features:
- Multi-stage build for minimal size
- Alpine Linux base with Node.js 24 (~200MB final image)
- Multi-platform support (amd64, arm64, arm/v7)
- Non-root user (UID 1001)
- Health check endpoint
- Proper signal handling with dumb-init
- Persistent data volume at
/app/data
Test Workflow (.github/workflows/tests.yml)
This workflow runs on pull requests to master, beta, and release branches.
Jobs
1. test - Electron Mode Tests
Tests the traditional Electron desktop application:
- Environment: Custom Docker container (
ghcr.io/thomasnordquist/mqtt-explorer-ui-tests:latest) - Steps:
- Install dependencies with frozen lockfile
- Build the Electron application
- Run unit tests (app + backend)
- Run UI tests with video recording
- Upload test video to S3 with 90-day expiration tag
- Post demo video to PR as comment
- Display test results in GitHub summary
Artifacts:
- UI test video (GIF format) uploaded to S3
- Video is tagged with
expiration=90daysfor automatic lifecycle deletion - Video is posted to the PR thread as an embedded image
- Videos expire after 90 days via S3 lifecycle policy
2. test-browser - Browser Mode Tests
Tests the new browser/server mode:
- Environment: Ubuntu latest with Node.js 20
- Services:
- Mosquitto MQTT Broker: Eclipse Mosquitto v2 on port 1883
- Health checks enabled
- Anonymous connections allowed
- Mosquitto MQTT Broker: Eclipse Mosquitto v2 on port 1883
- Steps:
- Setup Node.js 20
- Install dependencies
- Build browser mode (
yarn build:server) - Run unit tests (app + backend)
- Start server in background with test credentials
- Wait for server to be ready
- Run browser smoke tests
- Clean up server process
Environment Variables:
MQTT_EXPLORER_USERNAME=testMQTT_EXPLORER_PASSWORD=test123PORT=3000
Test Commands
The following npm scripts are used in CI/CD:
# Unit tests
yarn test # Run all tests (app + backend)
yarn test:app # Frontend tests only
yarn test:backend # Backend tests only
# Build
yarn build # Build Electron mode
yarn build:server # Build browser mode
# UI Tests (Electron only)
yarn ui-test # Run UI tests with video recording
Adding New Tests
For Electron Mode
Add tests to the test job. UI tests should be added to the test suite that yarn ui-test runs.
For Browser Mode
Browser-specific tests should:
- Use the pre-configured Mosquitto service
- Connect to
mqtt://mosquitto:1883 - Test server endpoints at
http://localhost:3000
Example:
- name: Browser Integration Test
run: |
# Test MQTT connection through server
curl -X POST http://localhost:3000/api/test
Local Testing
Docker Browser Mode
# Build the image locally (for your platform)
docker build -f Dockerfile.browser -t mqtt-explorer:local .
# Build for specific platform (e.g., Raspberry Pi)
docker buildx build --platform linux/arm64 -f Dockerfile.browser -t mqtt-explorer:local-arm64 .
# Run the container
docker run -d \
-p 3000:3000 \
-e MQTT_EXPLORER_USERNAME=test \
-e MQTT_EXPLORER_PASSWORD=test123 \
mqtt-explorer:local
# Test the server
curl http://localhost:3000
# Check logs
docker logs <container-id>
# Stop and remove
docker stop <container-id>
docker rm <container-id>
See DOCKER.md for complete documentation.
Electron Mode
yarn build
yarn test
yarn ui-test
Browser Mode
# Start Mosquitto in Docker
docker run -d -p 1883:1883 eclipse-mosquitto:2
# Build and test
yarn build:server
yarn test
# Start server
MQTT_EXPLORER_USERNAME=test MQTT_EXPLORER_PASSWORD=test123 yarn start:server
# Run manual tests
curl http://localhost:3000
GitHub Codespaces / Devcontainer
The repository includes a devcontainer configuration that automatically sets up:
- Node.js 20
- MQTT broker (Mosquitto)
- All development dependencies
- Port forwarding for development
See .devcontainer/README.md for details.
S3 Configuration for Demo Videos
Required S3 Lifecycle Policy
Demo videos uploaded from PRs are tagged with expiration=90days and require an S3 lifecycle policy to automatically delete them after 90 days.
Important: The video.mp4 file in the gh-pages branch is NOT tagged and will NOT expire.
Setting up the Lifecycle Policy
- Create a file named
s3-lifecycle-pr-videos.json:
{
"Rules": [
{
"ID": "ExpirePRDemoVideosAfter90Days",
"Status": "Enabled",
"Filter": {
"Tag": {
"Key": "expiration",
"Value": "90days"
}
},
"Expiration": {
"Days": 90
}
}
]
}
- Apply the policy to your S3 bucket:
aws s3api put-bucket-lifecycle-configuration \
--bucket YOUR_BUCKET_NAME \
--lifecycle-configuration file://s3-lifecycle-pr-videos.json
- Verify the policy:
aws s3api get-bucket-lifecycle-configuration --bucket YOUR_BUCKET_NAME
How It Works
- PR demo videos: Uploaded with filename pattern
pr-{number}-{timestamp}.gifand tagged with:expiration=90days- Used by lifecycle policy for automatic deletionSource=github-actions- Identifies source of uploadType=pr-demo-video- Categorizes the object type
- S3 lifecycle rule: Automatically deletes objects tagged with
expiration=90daysafter 90 days - Upload mechanism: Uses
ramonpaolo/action-upload-s3@mainGitHub Action with object tagging support - gh-pages video:
video.mp4in gh-pages branch is served from GitHub Pages, not S3, so it persists indefinitely
Required AWS Credentials
The workflow requires the following secrets/variables:
vars.AWS_KEY_ID- AWS access key ID (requiress3:PutObjectands3:PutObjectTaggingpermissions)secrets.AWS_SECRET_ACCESS_KEY- AWS secret access keyvars.AWS_BUCKET- S3 bucket name- AWS region:
eu-central-1(hardcoded in workflow)
The S3 bucket must have:
- Public read access enabled for uploaded objects
- Object tagging enabled
- Lifecycle policy configured as described above
Troubleshooting
Browser Tests Failing
- Server won't start: Check if port 3000 is already in use
- MQTT connection fails: Ensure Mosquitto service is healthy
- Timeout errors: Increase timeout in "Wait for Server" step
Electron Tests Failing
- UI tests timeout: Check if the Docker container has display access
- Build fails: Verify all dependencies are in yarn.lock
Future Improvements
- Add E2E browser tests with Playwright
- Test WebSocket connections in browser mode
- Add performance benchmarks
- Test with different MQTT broker versions
- Add security scanning for browser mode