mirror of
https://github.com/screentinker/screentinker.git
synced 2026-06-14 18:22:46 -06:00
- docs/openapi.yaml: the public, token-reachable surface only, with the auth model (Bearer st_) and a per-operation x-required-scope (read<write<full). JWT-only routers are excluded by design. - Serve /openapi.yaml + /docs (Redoc via a vendored standalone bundle, no CDN so it works air-gapped; /docs is CSP-exempt). docs/ is bundled into the release tarball. - CI: redocly lint + a public-only guard that fails loudly if a JWT-only path ever leaks into the spec. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
174 lines
7 KiB
YAML
174 lines
7 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: |
|
|
VERSION="$(cat VERSION)"
|
|
echo "version=$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"
|
|
# #80: a version carrying a -suffix (e.g. 1.9.0-rc1) is a pre-release.
|
|
case "$VERSION" in *-*) PRE=true ;; *) PRE=false ;; esac
|
|
echo "prerelease=$PRE" >> "$GITHUB_OUTPUT"
|
|
echo "Releasing ${GITHUB_REF_NAME} (version $VERSION, prerelease=$PRE); previous tag: ${PREV:-<none>}"
|
|
|
|
- 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
|
|
cp tizen/ScreenTinker.wgt ScreenTinker.wgt
|
|
ls -la ScreenTinker.wgt
|
|
|
|
- name: Build source tarball (bundles the .wgt; the signed apk is added by scripts/finalize-release.sh)
|
|
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' \
|
|
server frontend scripts docs VERSION README.md LICENSE .env.example ScreenTinker.wgt
|
|
echo "TARBALL=$OUT" >> "$GITHUB_ENV"
|
|
ls -la "$OUT"
|
|
|
|
- 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}\` - bundle: server + frontend source + the Tizen .wgt (the signed Android APK is added at the root during release finalization)."
|
|
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)."
|
|
if [ "${{ steps.ver.outputs.prerelease }}" = "true" ]; then
|
|
echo "- Docker image: \`ghcr.io/screentinker/screentinker:${{ steps.ver.outputs.version }}\` (pre-release - \`:latest\` is NOT moved)."
|
|
else
|
|
echo "- Docker image: \`ghcr.io/screentinker/screentinker:${{ steps.ver.outputs.version }}\` (also \`:latest\`)."
|
|
fi
|
|
echo "- \`ScreenTinker.apk\` - signed Android player (attached during release finalization)."
|
|
} > RELEASE_NOTES.md
|
|
cat RELEASE_NOTES.md
|
|
|
|
- name: Create GitHub Release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
# #80: pre-release tags publish as a GitHub *pre-release* (not "Latest"),
|
|
# which also keeps the /releases/latest API pointing at the last stable.
|
|
PRERELEASE_FLAG=""
|
|
[ "${{ steps.ver.outputs.prerelease }}" = "true" ] && PRERELEASE_FLAG="--prerelease"
|
|
gh release create "${{ steps.ver.outputs.tag }}" \
|
|
$PRERELEASE_FLAG \
|
|
--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: |
|
|
VERSION="$(cat VERSION)"
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
# #80: move :latest only for final releases - a pre-release (1.9.0-rc1) must
|
|
# not repoint :latest onto untested code (anyone on :latest pulls it on restart).
|
|
TAGS="ghcr.io/screentinker/screentinker:$VERSION"
|
|
case "$VERSION" in
|
|
*-*) echo "Pre-release $VERSION: :latest will NOT be moved" ;;
|
|
*) TAGS="${TAGS}"$'\n'"ghcr.io/screentinker/screentinker:latest" ;;
|
|
esac
|
|
{ echo "tags<<__EOF__"; printf '%s\n' "$TAGS"; echo "__EOF__"; } >> "$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: ${{ steps.ver.outputs.tags }}
|
|
|
|
# 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).
|