name: Release BrowserOS Server on: workflow_dispatch: inputs: version: description: "Release version (e.g. 0.0.80)" required: true type: string concurrency: group: release-server cancel-in-progress: false jobs: release: if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: release-core permissions: contents: write defaults: run: working-directory: packages/browseros-agent steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: oven-sh/setup-bun@v2 with: bun-version: "1.3.6" - name: Install dependencies run: bun ci - name: Prepare production env file run: cp apps/server/.env.production.example apps/server/.env.production - name: Validate version id: version env: REQUESTED_VERSION: ${{ inputs.version }} run: | PACKAGE_VERSION=$(node -p "require('./apps/server/package.json').version") echo "package_version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT" echo "release_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" if [ "$PACKAGE_VERSION" != "$REQUESTED_VERSION" ]; then echo "Requested version $REQUESTED_VERSION does not match apps/server/package.json ($PACKAGE_VERSION)" exit 1 fi - name: Build release artifacts run: bun run build:server:ci - name: Verify release artifacts run: | mapfile -t ZIP_FILES < <(find dist/prod/server -maxdepth 1 -type f -name 'browseros-server-resources-*.zip' | sort) if [ "${#ZIP_FILES[@]}" -eq 0 ]; then echo "No server release zip files were produced" exit 1 fi printf 'Found release artifacts:\n%s\n' "${ZIP_FILES[@]}" - name: Generate release notes env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PACKAGE_VERSION: ${{ steps.version.outputs.package_version }} run: | SERVER_APP_PATH="packages/browseros-agent/apps/server" SERVER_BUILD_DIR="packages/browseros-agent/scripts/build/server" SERVER_BUILD_ENTRY="packages/browseros-agent/scripts/build/server.ts" SERVER_RESOURCE_MANIFEST="packages/browseros-agent/scripts/build/config/server-prod-resources.json" SERVER_WORKSPACE_PKG="packages/browseros-agent/package.json" CURRENT_TAG="browseros-server-v$PACKAGE_VERSION" PREV_TAG=$(git tag -l "browseros-server-v*" --sort=-v:refname | grep -v "^${CURRENT_TAG}$" | head -n 1) if [ -z "$PREV_TAG" ]; then echo "Initial release of browseros-server." > /tmp/release-notes.md else COMMITS=$(git log "$PREV_TAG"..HEAD --pretty=format:"%H" -- \ "$SERVER_APP_PATH" \ "$SERVER_BUILD_DIR" \ "$SERVER_BUILD_ENTRY" \ "$SERVER_RESOURCE_MANIFEST" \ "$SERVER_WORKSPACE_PKG") if [ -z "$COMMITS" ]; then echo "No notable changes." > /tmp/release-notes.md else echo "## What's Changed" > /tmp/release-notes.md echo "" >> /tmp/release-notes.md while IFS= read -r SHA; do SUBJECT=$(git log -1 --pretty=format:"%s" "$SHA") PR_NUM=$(gh api "/repos/${{ github.repository }}/commits/${SHA}/pulls" --jq '.[0].number // empty' 2>/dev/null) if [ -n "$PR_NUM" ] && ! echo "$SUBJECT" | grep -qF "(#${PR_NUM})"; then echo "- ${SUBJECT} (#${PR_NUM})" >> /tmp/release-notes.md else echo "- ${SUBJECT}" >> /tmp/release-notes.md fi done <<< "$COMMITS" fi fi working-directory: ${{ github.workspace }} - name: Create GitHub release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PACKAGE_VERSION: ${{ steps.version.outputs.package_version }} RELEASE_SHA: ${{ steps.version.outputs.release_sha }} run: | TAG="browseros-server-v$PACKAGE_VERSION" TITLE="BrowserOS Server - v$PACKAGE_VERSION" mapfile -t ZIP_FILES < <(find packages/browseros-agent/dist/prod/server -maxdepth 1 -type f -name 'browseros-server-resources-*.zip' | sort) git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" if git rev-parse "$TAG" >/dev/null 2>&1; then echo "Tag $TAG already exists, skipping tag creation" else git tag -a "$TAG" -m "browseros-server v$PACKAGE_VERSION" "$RELEASE_SHA" fi if git ls-remote --tags origin "$TAG" | grep -q "$TAG"; then echo "Tag $TAG already on remote, skipping push" else git push origin "$TAG" fi if gh release view "$TAG" >/dev/null 2>&1; then echo "Release $TAG already exists, updating" gh release edit "$TAG" --title "$TITLE" --notes-file /tmp/release-notes.md gh release upload "$TAG" "${ZIP_FILES[@]}" --clobber else gh release create "$TAG" \ --title "$TITLE" \ --notes-file /tmp/release-notes.md \ "${ZIP_FILES[@]}" fi working-directory: ${{ github.workspace }}