diff --git a/.github/steps/setup-playwright/action.yml b/.github/steps/setup-playwright/action.yml new file mode 100644 index 000000000..6e5391e77 --- /dev/null +++ b/.github/steps/setup-playwright/action.yml @@ -0,0 +1,35 @@ +name: Set Up Playwright +description: Sets up Playwright +inputs: + working-directory: + description: Where to run + required: false +runs: + using: composite + steps: + # Adapted from https://playwrightsolutions.com/playwright-github-action-to-cache-the-browser-binaries/ + - name: Get installed Playwright version + id: playwright-version + run: echo version=$(yarn info --json @playwright/test | jq -r '.children.Version') >> $GITHUB_OUTPUT + working-directory: ${{ inputs.working-directory }} + shell: bash + + - name: Cache playwright binaries + uses: actions/cache@v4 + id: playwright-cache + with: + path: | + ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }} + + - name: Install Playwright Browsers + run: yarn playwright install --with-deps + working-directory: ${{ inputs.working-directory }} + if: steps.playwright-cache.outputs.cache-hit != 'true' + shell: bash + + - name: Install Playwright OS dependencies + run: npx playwright install-deps + working-directory: ${{ inputs.working-directory }} + if: steps.playwright-cache.outputs.cache-hit == 'true' + shell: bash diff --git a/.github/workflows/build_shared.yml b/.github/workflows/build_shared.yml index 9f36d014f..58e768973 100644 --- a/.github/workflows/build_shared.yml +++ b/.github/workflows/build_shared.yml @@ -8,7 +8,10 @@ on: tag: type: string required: true - description: Docker tag to push + description: Version to build, also the docker tag + strip_rc: + type: boolean + description: Strip -rc suffix from version number permissions: contents: read @@ -34,7 +37,17 @@ jobs: - run: yarn install --immutable - - run: "yarn package" + - name: Set version + shell: pwsh + run: | + $pkg = Get-Content ./desktop/package.json | ConvertFrom-Json + $pkg.version = "${{inputs.tag }}" -replace "^v", "" + if ("${{ inputs.strip_rc }}" -eq "true") { + $pkg.version = $pkg.version -replace "-rc.*", "" + } + $pkg | ConvertTo-Json -Depth 32 | Set-Content ./desktop/package.json + + - run: "yarn package --win --publish never" working-directory: ./desktop env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -67,6 +80,15 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Set version + run: | + set -x + version=$(echo '${{ inputs.tag }}' | sed 's/^v//') + if [[ '${{ inputs.strip_rc }}' == 'true' ]]; then + version=$(echo $version | sed 's/-rc.*//') + fi + cat package.json | jq '.version = "'$version'"' | tee package.json + working-directory: ./jobrunner - name: Docker metadata id: meta uses: docker/metadata-action@v5 @@ -74,8 +96,7 @@ jobs: images: ghcr.io/ystv/badger/server flavor: latest=true tags: | - type=semver,pattern={{version}},enable=${{ github.event_name == 'workflow_dispatch' }} - type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_call' }} + type=raw,value=${{ inputs.tag }} - name: Build and push uses: docker/build-push-action@v6 with: @@ -101,6 +122,13 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Set version + run: | + version=$(echo '${{ inputs.tag }}' | sed 's/^v//') + if [[ '${{ inputs.strip_rc }}' == 'true' ]]; then + version=$(echo $version | sed 's/-rc.*//') + fi + cat package.json | jq '.version = "'$version'"' | tee package.json - name: Docker metadata id: jr_meta uses: docker/metadata-action@v5 @@ -108,8 +136,7 @@ jobs: images: ghcr.io/ystv/badger/jobrunner flavor: latest=true tags: | - type=semver,pattern={{version}},enable=${{ github.event_name == 'workflow_dispatch' }} - type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_call' }} + type=raw,value=${{ inputs.tag }} - name: Build and push uses: docker/build-push-action@v6 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d21ce512f..e8b607da5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,16 +3,15 @@ on: tags: - "*-rc*" -permissions: - contents: write - packages: write +permissions: write-all jobs: build: uses: ./.github/workflows/build_shared.yml with: - ref: ${{ github.event.before }} - tag: ${{ github.event.ref }} + ref: ${{ github.ref }} + tag: ${{ github.ref_name }} + strip_rc: true test-e2e-server: needs: [build] @@ -31,36 +30,24 @@ jobs: cache-dependency-path: "yarn.lock" - name: Set ref in docker-compose - run: sed -i "s/__RC_REF__/${{ github.event.ref }}/g" docker-compose-rc-test.yml + run: sed -i "s/__RC_REF__/${{ github.ref_name }}/g" docker-compose-rc-test.yml - name: Start services - run: docker compose up -d -f docker-compose.yml -f docker-compose-rc-test.yml + run: docker compose -f docker-compose.yml -f docker-compose-rc-test.yml up -d - # Adapted from https://playwrightsolutions.com/playwright-github-action-to-cache-the-browser-binaries/ - - name: Get installed Playwright version - id: playwright-version - run: echo version=$(yarn info --json @playwright/test | jq -r '.children.Version') >> $GITHUB_OUTPUT - working-directory: ./server + - run: yarn install --immutable --inline-builds - - name: Cache playwright binaries - uses: actions/cache@v4 - id: playwright-cache + - uses: ./.github/steps/setup-playwright with: - path: | - ~/.cache/ms-playwright - key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }} - - - name: Install Playwright Browsers - run: yarn playwright install --with-deps - working-directory: ./server - if: steps.playwright-cache.outputs.cache-hit != 'true' - - - name: Install Playwright OS dependencies - run: npx playwright install-deps - if: steps.playwright-cache.outputs.cache-hit == 'true' + working-directory: ./server - name: Migrate database - run: yarn prisma:migrateProd + run: | + yarn prisma:migrateProd + + - name: Retart services + run: | + docker compose -f docker-compose.yml -f docker-compose-rc-test.yml restart server jobrunner - name: Run Playwright tests run: yarn ${{ runner.debug && 'test:e2e:debug' || 'test:e2e' }} @@ -75,31 +62,114 @@ jobs: path: ./server/playwright-report/ retention-days: 30 + test-desktop: + runs-on: windows-latest + needs: [build] + steps: + - uses: actions/checkout@v4 + - name: Use Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: "yarn" + cache-dependency-path: "yarn.lock" + - name: Download Desktop build + uses: actions/download-artifact@v4 + with: + name: badger-desktop-windows + - name: Install Badger + run: | + $version = "${{ github.ref_name }}" -replace "^v", "" -replace "-rc.*", "" + Start-Process -FilePath "Badger Desktop-$version.exe" -ArgumentList "/S","/D=${{ runner.temp }}\badger" -Wait + shell: pwsh + - run: yarn install --immutable --inline-builds + - name: Run tests + run: yarn test:e2e --project=standalone + working-directory: ./desktop + env: + TEST_APPLICATION_PATH: ${{ runner.temp }}\badger\Badger Desktop.exe + + linear: + needs: [test-e2e-server, test-desktop] + runs-on: ubuntu-latest + outputs: + issue_id: ${{ steps.issue.outputs.issue_id }} + steps: + - name: Determine version number + run: echo "VERSION=$(echo '${{ github.ref_name }}' | sed 's/-rc.*//')" >> $GITHUB_ENV + - uses: actions/setup-node@v3 + with: + node-version: "20.x" + - run: npm install @linear/sdk + - name: Create Linear release ticket + id: issue + uses: actions/github-script@v7 + with: + script: | + const { LinearClient } = require('@linear/sdk'); + const lin = new LinearClient({ + accessToken: "${{ secrets.LINEAR_ACCESS_TOKEN }}" + }); + const issueCreate = await lin.createIssue({ + teamId: "${{ vars.LINEAR_TEAM_ID }}", + templateId: "${{ vars.LINEAR_RELEASE_ISSUE_TEMPLATE_ID }}", + stateId: "${{ vars.LINEAR_TODO_STATE_ID }}", + title: `Release ${{ github.ref_name }}`, + }); + if (!issueCreate.success) { + throw new Error(`Failed to create issue`); + } + const issue = await issueCreate.issue; + + await lin.createComment({ + issueId: issue.id, + body: `Artifacts: + Server Docker image: \`ghcr.io/ystv/badger/server:${{ github.ref_name }}\` + Jobrunner Docker Image: \`ghcr.io/ystv/badger/jobrunner:${{ github.ref_name }}\` + Desktop Windows installer: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + Once testing is complete, please approve [this workflow](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). + `.replace(/^\s*/gm, ''), + }); + + core.summary.addHeading('Linear issue'); + core.summary.addLink(issue.identifier, issue.url); + core.setOutput('issue_id', issue.id); + release: - needs: [test-e2e-server, build] + needs: [test-e2e-server, test-desktop, linear] environment: release runs-on: ubuntu-latest + permissions: write-all steps: - name: Determine version number - run: echo "VERSION=$(echo '${{ github.event.ref }}' | sed 's/^refs\/tags\///' | sed 's/-rc.*//')" >> $GITHUB_ENV + run: echo "VERSION=$(echo '${{ github.ref_name }}' | sed 's/-rc.*//')" >> $GITHUB_ENV - name: Download Desktop build uses: actions/download-artifact@v4 with: + pattern: badger-desktop-* path: artifacts - - name: Pull Docker images - run: | - docker pull ghcr.io/ystv/badger/server:${{ github.event.ref }} - docker pull ghcr.io/ystv/badger/jobrunner:${{ github.event.ref }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Re-tag and push Docker images - run: - docker tag ghcr.io/ystv/badger/server:${{ github.event.ref }} ghcr.io/ystv/badger/server:$VERSION - docker tag ghcr.io/ystv/badger/jobrunner:${{ github.event.ref }} ghcr.io/ystv/badger/jobrunner:$VERSION - docker push ghcr.io/ystv/badger/server:$VERSION - docker push ghcr.io/ystv/badger/jobrunner:$VERSION + run: | + for img in server jobrunner; do + docker pull ghcr.io/ystv/badger/$img:${{ github.ref_name }} + docker tag ghcr.io/ystv/badger/$img:${{ github.ref_name }} ghcr.io/ystv/badger/$img:$VERSION + docker push ghcr.io/ystv/badger/$img:$VERSION + docker tag ghcr.io/ystv/badger/$img:${{ github.ref_name }} ghcr.io/ystv/badger/$img:latest + docker push ghcr.io/ystv/badger/$img:latest + done + shell: bash - name: Create GitHub release uses: actions/github-script@v7 id: release with: + github-token: ${{ secrets.GH_RELEASE_PAT }} script: | const release = await github.rest.repos.createRelease({ owner: context.repo.owner, @@ -109,21 +179,39 @@ jobs: name: process.env.VERSION, draft: true, generate_release_notes: true, - make_latest: true + prerelease: false }); core.setOutput('id', release.data.id) - core.setOutput('upload_url', release.data.upload_url) core.setOutput('tag_name', release.data.tag_name) - name: Upload artifacts run: | find artifacts -type f -exec gh release upload ${{ steps.release.outputs.tag_name }} {} \; + env: + GITHUB_TOKEN: ${{ secrets.GH_RELEASE_PAT }} - name: Publish release uses: actions/github-script@v7 with: + github-token: ${{ secrets.GITHUB_TOKEN }} script: | await github.rest.repos.updateRelease({ owner: context.repo.owner, repo: context.repo.repo, - release_id: ${{ steps.release.outputs.id }}, + release_id: "${{ steps.release.outputs.id }}", draft: false }) + - uses: actions/setup-node@v3 + with: + node-version: "20.x" + - run: npm install @linear/sdk + - name: Close Linear issue + uses: actions/github-script@v7 + with: + script: | + const { LinearClient } = require('@linear/sdk'); + const lin = new LinearClient({ + accessToken: "${{ secrets.LINEAR_ACCESS_TOKEN }}" + }); + await lin.updateIssue({ + issueId: "${{ needs.linear.outputs.issue_id }}", + stateId: "${{ vars.LINEAR_DONE_STATE_ID }}", + }); diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 23985672b..d9f938c8c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -162,28 +162,9 @@ jobs: cp ../utility/prisma/schema.prisma . working-directory: ./jobrunner - # Adapted from https://playwrightsolutions.com/playwright-github-action-to-cache-the-browser-binaries/ - - name: Get installed Playwright version - id: playwright-version - run: echo version=$(yarn info --json @playwright/test | jq -r '.children.Version') >> $GITHUB_OUTPUT - working-directory: ./server - - - name: Cache playwright binaries - uses: actions/cache@v4 - id: playwright-cache + - uses: ./.github/steps/setup-playwright with: - path: | - ~/.cache/ms-playwright - key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }} - - - name: Install Playwright Browsers - run: yarn playwright install --with-deps - working-directory: ./server - if: steps.playwright-cache.outputs.cache-hit != 'true' - - - name: Install Playwright OS dependencies - run: npx playwright install-deps - if: steps.playwright-cache.outputs.cache-hit == 'true' + working-directory: ./server - name: Migrate database run: yarn prisma:migrateProd diff --git a/desktop/e2e/standalone/base.ts b/desktop/e2e/standalone/base.ts index 0b8e0f770..53bdcb568 100644 --- a/desktop/e2e/standalone/base.ts +++ b/desktop/e2e/standalone/base.ts @@ -35,8 +35,13 @@ const test = base.extend<{ }, app: async ({ scenario, testMediaPath }, use, testInfo) => { + // Allow running tests on a built / installed app + const electronPath = process.env.TEST_APPLICATION_PATH; const app = await electron.launch({ - args: ["--enable-logging", "out/main/index.js"], + args: electronPath + ? ["--enable-logging"] + : ["--enable-logging", "out/main/index.js"], + executablePath: electronPath, env: { ...process.env, NODE_ENV: "test", diff --git a/desktop/src/main/media/downloadFile.ts b/desktop/src/main/media/downloadFile.ts index fe248cac4..d54ab93dc 100644 --- a/desktop/src/main/media/downloadFile.ts +++ b/desktop/src/main/media/downloadFile.ts @@ -65,7 +65,7 @@ const CurlDownloader: Downloader = async function CurlDownloader( ) { invariant(curlPath, "no curl path"); logger.info("Using curl downloader"); - const args = ["-f", "--compressed", "-o", outputPath, url]; + const args = ["-f", "--compressed", "--location", "-o", outputPath, url]; logger.info(`Curl command: ${curlPath} ${args.join(" ")}`); const proc = spawn(curlPath, args); if (progressCB) { diff --git a/docker-compose-rc-test.yml b/docker-compose-rc-test.yml index 5a017e151..7dc39ae0e 100644 --- a/docker-compose-rc-test.yml +++ b/docker-compose-rc-test.yml @@ -1,3 +1,4 @@ +# Used by the release GitHub Actions workflow. version: "3" services: server: @@ -14,7 +15,7 @@ services: AWS_SECRET_ACCESS_KEY: "rootroot" AWS_REGION: "us-east-1" STORAGE_BUCKET: "badger" - API_SHARED_SECRET: "password" + API_SHARED_SECRET: "aaa" PUBLIC_URL: "http://localhost:3000" JWT_SIGNING_KEY: "somesecret" NODE_ENV: "test" @@ -24,4 +25,6 @@ services: image: ghcr.io/ystv/badger/jobrunner:__RC_REF__ platform: linux/amd64 command: ["--watch", "--healthPort", "28342"] # matching server/playwright.config.ts + ports: + - "28342:28342" environment: *badger_env diff --git a/server/app/layout.tsx b/server/app/layout.tsx index 7f103613a..b80f725c5 100644 --- a/server/app/layout.tsx +++ b/server/app/layout.tsx @@ -9,7 +9,7 @@ import { checkSession } from "@/lib/auth"; import { UserProvider } from "@/components/CurrentUser"; import Script from "next/script"; import { FeatureFlagsProvider } from "@/components/FeatureFlags"; -import { getTusEndpoint } from "@/lib/tus"; +import { getPublicTusEndpoint } from "@/lib/tus"; import { UploadProgress } from "@/components/Uploader"; export const metadata: Metadata = { @@ -32,7 +32,7 @@ export default async function RootLayout({ {`window.ENVIRONMENT = ${JSON.stringify(process.env.ENVIRONMENT)};`} diff --git a/server/app/shows/[show_id]/rundown/[rundown_id]/page.tsx b/server/app/shows/[show_id]/rundown/[rundown_id]/page.tsx index f843e4689..5899a8372 100644 --- a/server/app/shows/[show_id]/rundown/[rundown_id]/page.tsx +++ b/server/app/shows/[show_id]/rundown/[rundown_id]/page.tsx @@ -4,7 +4,7 @@ import { RundownItems } from "./RundownItems"; import RundownAssets from "./RundownAssets"; import Link from "next/link"; import { TusEndpointProvider } from "@/components/MediaUpload"; -import { getTusEndpoint } from "@/lib/tus"; +import { getPublicTusEndpoint } from "@/lib/tus"; import { Suspense, cache } from "react"; import { Button } from "@badger/components/button"; import { MetadataTargetType } from "@badger/prisma/client"; @@ -176,7 +176,7 @@ export default async function RundownPage(props: {

{rundown.name}

- + Loading...}> diff --git a/server/e2e/lib.ts b/server/e2e/lib.ts index 96219089c..dc8cc33cc 100644 --- a/server/e2e/lib.ts +++ b/server/e2e/lib.ts @@ -4,7 +4,7 @@ import { PutObjectCommand, S3Client, } from "@aws-sdk/client-s3"; -import { Page, expect, test as base, Request } from "@playwright/test"; +import { Page, expect, test as base } from "@playwright/test"; import { CreateTRPCProxyClient, createTRPCProxyClient, diff --git a/server/e2e/metadata.spec.ts b/server/e2e/metadata.spec.ts index 095b797aa..295206ca0 100644 --- a/server/e2e/metadata.spec.ts +++ b/server/e2e/metadata.spec.ts @@ -1,5 +1,10 @@ -import { expect } from "@playwright/test"; -import { test, getAPIClient, fileToDataTransfer, createShow } from "./lib"; +import { + test, + expect, + getAPIClient, + fileToDataTransfer, + createShow, +} from "./lib"; import { readFileSync } from "node:fs"; import path from "node:path"; diff --git a/server/e2e/showItems.spec.ts b/server/e2e/showItems.spec.ts index 0bf00bc27..af600f4cd 100644 --- a/server/e2e/showItems.spec.ts +++ b/server/e2e/showItems.spec.ts @@ -1,7 +1,12 @@ -import { expect } from "@playwright/test"; import { readFileSync } from "fs"; import * as path from "node:path"; -import { test, fileToDataTransfer, createShow, createMedia } from "./lib"; +import { + test, + expect, + fileToDataTransfer, + createShow, + createMedia, +} from "./lib"; test("add, reorder, remove items", async ({ showPage }) => { await expect diff --git a/server/e2e/shows.spec.ts b/server/e2e/shows.spec.ts index 042547b4a..c6f2337f4 100644 --- a/server/e2e/shows.spec.ts +++ b/server/e2e/shows.spec.ts @@ -1,5 +1,4 @@ -import { test, expect } from "@playwright/test"; -import { createShowAPI } from "./lib"; +import { test, expect, createShowAPI } from "./lib"; test("loads", async ({ page }) => { await page.goto("/");