Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 43 additions & 10 deletions .github/workflows/release-pr-checks.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# Runs CI and CodeQL on release-please PRs.
#
# Why this exists: release-please creates PRs using GITHUB_TOKEN, which does
# NOT trigger other workflows (pull_request events). pull_request_target IS
# triggered by GITHUB_TOKEN events because it runs in the base branch context.
# Without this, release PRs have no status checks and can't be merged when
# branch protection requires them.
# NOT trigger other workflows (pull_request events). Without this workflow,
# release PRs have no status checks and can't be merged when branch protection
# requires them.
#
# Triggers:
# 1. pull_request_target (opened/synchronize/reopened) — fired by close/reopen
# in release.yml or by direct PR updates
# 2. workflow_run (Release workflow completed) — fallback if close/reopen fails
# to fire pull_request_target events
# How it works:
# 1. workflow_run trigger fires reliably when the Release workflow completes.
# This is the primary mechanism — it always fires regardless of token type.
# 2. pull_request_target is a secondary trigger for manual close/reopen from
# the GitHub UI (useful as a manual fallback if needed).
# 3. When triggered via workflow_run, check runs are associated with the main
# branch commit (not the PR head). Branch protection can't see them, so the
# ci job posts commit statuses to the PR head SHA matching the required
# check names ("ci" and "lint-and-check").
#
# Security: restricted to release-please branches only. The checkout uses the
# PR's HEAD SHA, which is safe because release-please PRs come from within the
Expand All @@ -26,7 +29,10 @@ on:
workflows: [Release]
types: [completed]

permissions: read-all
permissions:
contents: read
statuses: write
security-events: write

jobs:
# Gate: only run for release-please PRs. Finds PR details from either trigger.
Expand Down Expand Up @@ -112,6 +118,33 @@ jobs:
fi
echo "CI passed"

# When triggered via workflow_run, check runs are associated with the main
# branch commit, not the PR head. Branch protection can't see them, so we
# post commit statuses to the PR head SHA matching the required check names.
- name: Report status to release PR
if: needs.should-run.outputs.head_sha != '' && github.event_name == 'workflow_run'
env:
GH_TOKEN: ${{ github.token }}
run: |
HEAD_SHA="${{ needs.should-run.outputs.head_sha }}"
RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"

if [[ "${{ needs.lint-and-check.result }}" == "failure" || "${{ needs.lint-and-check.result }}" == "cancelled" ]]; then
STATE="failure"
DESC="Release PR checks failed"
else
STATE="success"
DESC="Release PR checks passed"
fi

echo "Posting $STATE status to $HEAD_SHA for required checks..."
for CHECK in "ci" "lint-and-check"; do
gh api "repos/${{ github.repository }}/statuses/$HEAD_SHA" \
-f state="$STATE" -f context="$CHECK" \
-f description="$DESC" -f target_url="$RUN_URL"
echo " Posted $CHECK: $STATE"
done

codeql:
needs: [should-run]
if: needs.should-run.outputs.head_sha != ''
Expand Down
71 changes: 5 additions & 66 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ on:
push:
branches: [main]

# Note: release-please uses GITHUB_TOKEN, which doesn't trigger other workflows.
# CI/CodeQL checks on release PRs are handled by release-pr-checks.yml using
# pull_request_target. The close/reopen step below ensures the `reopened` event
# fires reliably (force-push `synchronize` events are inconsistent with GITHUB_TOKEN).
# Docker publishing is handled by docker-publish.yml using workflow_run.
#
# Ideal fix: use a fine-grained PAT (repo secret RELEASE_TOKEN) with contents:write
# and pull-requests:write. This makes release-please PRs trigger normal pull_request
# events, eliminating the need for release-pr-checks.yml entirely.
# Note: release-please uses GITHUB_TOKEN, which can't trigger other workflows
# (GitHub limitation). CI checks on release PRs are handled by
# release-pr-checks.yml via workflow_run (fires when this workflow completes).
# That workflow posts commit statuses to the PR head SHA so branch protection
# sees the results. Docker publishing uses docker-publish.yml (also workflow_run).

permissions:
contents: write
Expand All @@ -31,60 +27,3 @@ jobs:
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

# When release-please creates or updates a PR via GITHUB_TOKEN, the
# pull_request_target `synchronize` event doesn't always fire. Closing
# and reopening the PR ensures the `reopened` event triggers CI checks.
# We verify both state transitions and retry once on failure.
trigger-pr-checks:
needs: [release-please]
if: needs.release-please.outputs.pr != '' && needs.release-please.outputs.release_created != 'true'
runs-on: ubuntu-latest
steps:
- name: Close and reopen release PR to trigger checks
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ fromJSON(needs.release-please.outputs.pr).number }}
run: |
trigger_checks() {
echo "Closing release PR #$PR_NUMBER..."
gh pr close "$PR_NUMBER" --repo "$GITHUB_REPOSITORY"

# Wait and verify closed state
for i in 1 2 3; do
sleep 3
STATE=$(gh pr view "$PR_NUMBER" --json state --jq '.state' --repo "$GITHUB_REPOSITORY")
if [ "$STATE" = "CLOSED" ]; then break; fi
echo " Waiting for close to propagate (attempt $i)..."
done

echo "Reopening release PR #$PR_NUMBER..."
gh pr reopen "$PR_NUMBER" --repo "$GITHUB_REPOSITORY"

# Verify reopened state
sleep 3
STATE=$(gh pr view "$PR_NUMBER" --json state --jq '.state' --repo "$GITHUB_REPOSITORY")
if [ "$STATE" = "OPEN" ]; then
echo "Release PR #$PR_NUMBER successfully reopened"
return 0
else
echo "WARNING: PR state is $STATE after reopen attempt"
return 1
fi
}

# First attempt
if trigger_checks; then
exit 0
fi

echo "First attempt failed, retrying in 10s..."
sleep 10

# Retry once
if trigger_checks; then
exit 0
fi

echo "ERROR: Failed to reopen PR after 2 attempts"
exit 1
9 changes: 8 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ Request: { "url": "https://tiktok.com/...", "phone": "+1234567890" }
Response: { "ok": true, "clipId": "...", "status": "downloading" } (201 Created)
```

### GET /api/clips/[id]
Returns full clip detail with user context, interaction state, and metadata.
```
Response: { id, originalUrl, videoPath, audioPath, thumbnailPath, title, artist, albumArt, spotifyUrl, appleMusicUrl, youtubeMusicUrl, addedBy, addedByUsername, addedByAvatar, platform, status, contentType, durationSeconds, watched, favorited, reactions, commentCount, unreadCommentCount, viewCount, seenByOthers, createdAt, canEditCaption }
```

### PATCH /api/clips/[id]
```
Request: { "title": "new caption" }
Expand Down Expand Up @@ -157,9 +163,10 @@ Response: { "heartCount": 3, "hearted": true }
```

### POST /api/clips/[id]/reactions
Enforces one reaction per user per clip. Posting a different emoji replaces the user's previous reaction. Posting the same emoji removes it.
```
Request: { "emoji": "🔥" }
Response: { "reactions": { "🔥": { "count": 2, "reacted": true } } }
Response: { "reactions": { "🔥": { "count": 2, "reacted": true } }, "toggled": true }
```
Allowed emojis: ❤️ 👍 👎 😂 ‼️ ❓

Expand Down
1 change: 1 addition & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ scrolly/
│ │ │ ├── ReelMusic.svelte # Music player within a reel
│ │ │ ├── ReelOverlay.svelte # Bottom overlay (user info, caption)
│ │ │ ├── ActionSidebar.svelte # Right-side action buttons
│ │ │ ├── ClipOverlay.svelte # Full-screen single-clip overlay view
│ │ │ ├── CommentsSheet.svelte # Bottom sheet for comments
│ │ │ ├── ViewersSheet.svelte # Bottom sheet for view list
│ │ │ ├── AddVideoModal.svelte # Modal to paste/submit URLs
Expand Down
Loading