mirror of
https://github.com/screentinker/screentinker.git
synced 2026-06-19 04:32:31 -06:00
- .github/workflows/release.yml: on a v* tag - verify the tag matches VERSION (fail-fast guard), run tests, build a source tarball + the unsigned Tizen .wgt and publish a GitHub Release with generated notes, and build+push a multi-arch (amd64 + arm64) image to ghcr.io/screentinker/screentinker:<version> + :latest. The Release (artifacts) and the docker push are independent jobs, so an arm64/QEMU docker failure does not block the GitHub Release and is re-runnable. Nothing deploys to prod. APK-build-in-CI left as a TODO (keystore secret). - Dockerfile + .dockerignore: multi-stage node:20-slim image with server + frontend + VERSION + scripts; DATA_DIR=/data volume for db/uploads/jwt-secret. Verified to build, boot, serve the dashboard + web player, and persist state. - docker-compose.example.yml: /data volume, SELF_HOSTED, a node-fetch healthcheck against /api/status, and an admin-lockout recovery note (reset-admin.js). - server.js: resolve the OTA APK from DATA_DIR first (a container can mount one at /data/ScreenTinker.apk), fall back to the legacy in-repo path, 404 gracefully. - ci.yml: bump checkout/setup-node to v6 (clears the Node-20 action deprecation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
151 lines
5.5 KiB
YAML
151 lines
5.5 KiB
YAML
name: Release
|
|
|
|
# Fires when a version tag is pushed (e.g. v1.8.0). Builds + publishes artifacts
|
|
# only - nothing here deploys to production.
|
|
on:
|
|
push:
|
|
tags: ['v*']
|
|
|
|
permissions:
|
|
contents: write # create the GitHub Release
|
|
packages: write # push the image to ghcr.io
|
|
|
|
concurrency:
|
|
group: release-${{ github.ref }}
|
|
cancel-in-progress: false # never cancel a release mid-publish
|
|
|
|
jobs:
|
|
# Fail-fast: a hand-pushed tag that disagrees with VERSION must not publish
|
|
# anything (the artifacts would report the wrong version). Gates everything.
|
|
verify:
|
|
name: Verify tag matches VERSION
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- name: Assert pushed tag equals VERSION
|
|
run: |
|
|
TAG="${GITHUB_REF_NAME#v}"
|
|
FILE="$(cat VERSION)"
|
|
echo "pushed tag: ${GITHUB_REF_NAME} (stripped: $TAG) VERSION file: $FILE"
|
|
if [ "$TAG" != "$FILE" ]; then
|
|
echo "::error::Tag ${GITHUB_REF_NAME} does not match VERSION ($FILE) - refusing to publish."
|
|
exit 1
|
|
fi
|
|
echo "OK: tag matches VERSION ($FILE)"
|
|
|
|
test:
|
|
name: Tests
|
|
needs: verify
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: server
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: '20'
|
|
cache: npm
|
|
cache-dependency-path: server/package-lock.json
|
|
- run: npm ci
|
|
- run: npm test
|
|
|
|
artifacts:
|
|
name: Tarball + Tizen .wgt + GitHub Release
|
|
needs: test
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0 # full history, for release notes
|
|
|
|
- name: Resolve version + previous tag
|
|
id: ver
|
|
run: |
|
|
echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
|
|
echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
|
|
PREV="$(git describe --tags --abbrev=0 "${GITHUB_REF_NAME}^" 2>/dev/null || true)"
|
|
echo "prev=$PREV" >> "$GITHUB_OUTPUT"
|
|
echo "Releasing ${GITHUB_REF_NAME} (version $(cat VERSION)); previous tag: ${PREV:-<none>}"
|
|
|
|
- name: Build source tarball
|
|
run: |
|
|
OUT="screentinker-${{ steps.ver.outputs.version }}.tar.gz"
|
|
tar czf "$OUT" \
|
|
--exclude='node_modules' --exclude='.git' --exclude='.github' \
|
|
--exclude='*.db' --exclude='*.db-wal' --exclude='*.db-shm' --exclude='*.db.*' \
|
|
--exclude='server/uploads' --exclude='server/certs' --exclude='server/test' \
|
|
--exclude='*.apk' --exclude='*.wgt' \
|
|
server frontend scripts VERSION README.md LICENSE .env.example
|
|
echo "TARBALL=$OUT" >> "$GITHUB_ENV"
|
|
ls -la "$OUT"
|
|
|
|
- name: Build Tizen .wgt (unsigned in CI)
|
|
run: |
|
|
chmod +x tizen/build-wgt.sh
|
|
( cd tizen && ./build-wgt.sh ) # no Tizen CLI on the runner => unsigned zip
|
|
ls -la tizen/ScreenTinker.wgt
|
|
|
|
- name: Generate release notes
|
|
run: |
|
|
PREV="${{ steps.ver.outputs.prev }}"
|
|
{
|
|
echo "## ScreenTinker ${{ steps.ver.outputs.tag }}"
|
|
echo
|
|
echo "### Changes"
|
|
if [ -n "$PREV" ]; then
|
|
git log --no-merges --pretty='- %s' "${PREV}..${{ steps.ver.outputs.tag }}"
|
|
else
|
|
echo "_First tagged release. Most recent changes:_"
|
|
git log --no-merges --pretty='- %s' -n 30 "${{ steps.ver.outputs.tag }}"
|
|
fi
|
|
echo
|
|
echo "### Artifacts"
|
|
echo "- \`${TARBALL}\` - server + frontend source tarball (Node 20; see the README to run)."
|
|
echo "- \`ScreenTinker.wgt\` - Tizen TV web app, **unsigned - for inspection only**."
|
|
echo " Sign it with your own Samsung certificate (Tizen Studio + a profile that includes"
|
|
echo " your TV's DUID) to install, or - easiest - point a Tizen TV browser / URL Launcher"
|
|
echo " at \`https://<your-instance>/player\` (no signing needed)."
|
|
echo "- Docker image: \`ghcr.io/screentinker/screentinker:${{ steps.ver.outputs.version }}\` (also \`:latest\`)."
|
|
} > RELEASE_NOTES.md
|
|
cat RELEASE_NOTES.md
|
|
|
|
- name: Create GitHub Release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh release create "${{ steps.ver.outputs.tag }}" \
|
|
--title "ScreenTinker ${{ steps.ver.outputs.tag }}" \
|
|
--notes-file RELEASE_NOTES.md \
|
|
"${TARBALL}" \
|
|
tizen/ScreenTinker.wgt
|
|
|
|
docker:
|
|
name: Docker image (amd64 + arm64) -> ghcr
|
|
needs: test
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- id: ver
|
|
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
|
|
- uses: docker/setup-qemu-action@v3
|
|
- uses: docker/setup-buildx-action@v3
|
|
- uses: docker/login-action@v3
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
- uses: docker/build-push-action@v6
|
|
with:
|
|
context: .
|
|
platforms: linux/amd64,linux/arm64
|
|
push: true
|
|
tags: |
|
|
ghcr.io/screentinker/screentinker:${{ steps.ver.outputs.version }}
|
|
ghcr.io/screentinker/screentinker:latest
|
|
|
|
# TODO (deferred): build + sign the Android APK in CI. Requires the release
|
|
# keystore + passwords as encrypted Actions secrets. For now the maintainer
|
|
# attaches a signed APK out-of-band (and self-hosters mount one at
|
|
# /data/ScreenTinker.apk).
|