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
36 changes: 32 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,30 @@ jobs:
retention-days: 1

workflow-tests:
name: PocketBase, Electron and browser workflows
name: Integration workflows (${{ matrix.suite }} ${{ matrix.shard-index }}/${{ matrix.shard-total }})
runs-on: ubuntu-latest
timeout-minutes: 25
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
strategy:
fail-fast: false
matrix:
include:
- suite: electron
shard-index: 1
shard-total: 4
- suite: electron
shard-index: 2
shard-total: 4
- suite: electron
shard-index: 3
shard-total: 4
- suite: electron
shard-index: 4
shard-total: 4
- suite: web
shard-index: 1
shard-total: 1
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
Expand All @@ -250,28 +269,37 @@ jobs:
run: npm ci --prefer-offline

- name: Verify PocketBase replay against real storage
if: matrix.suite == 'web'
run: npm run test:pocketbase -- verification/offline-replay-real-pb.test.ts verification/dynatrace-pipeline.test.ts

- name: Install Playwright browsers and Linux dependencies
if: matrix.suite == 'web'
run: npx playwright install --with-deps chromium webkit

# Each npm runner restores the Node ABI before the next suite starts.
- name: Install Electron Linux dependencies
if: matrix.suite == 'electron'
run: npx playwright install-deps chromium

# Isolated runners avoid concurrent native rebuilds. Keep one worker per
# shard; fully-parallel distributes individual tests, including large specs.
- name: Run Electron workflows
if: matrix.suite == 'electron'
run: |
sudo apt-get install --yes dbus-x11 gnome-keyring
dbus-run-session -- bash -euo pipefail -c '
openssl rand -hex 32 | gnome-keyring-daemon --unlock --components=secrets
xvfb-run --auto-servernum npm run test:electron
xvfb-run --auto-servernum npm run test:electron -- --fully-parallel --workers=1 --shard=${{ matrix.shard-index }}/${{ matrix.shard-total }}
'

- name: Run browser workflows
if: matrix.suite == 'web'
run: xvfb-run --auto-servernum npm run test:web

- name: Upload workflow failure details
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: workflow-test-failures
name: workflow-test-failures-${{ matrix.suite }}-${{ matrix.shard-index }}
path: test-results/
if-no-files-found: ignore
retention-days: 1
Expand Down
80 changes: 80 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,26 @@ jobs:
fetch-depth: 0
ref: ${{ needs.determine.outputs.source-sha }}

- name: Resolve release test mode
id: mode
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
SOURCE_SHA: ${{ needs.determine.outputs.source-sha }}
TEST_TREE: ${{ vars.RELAY_RELEASE_TEST_TREE }}
with:
script: |
const { resolveReleaseTestMode } = await import(
`${process.env.GITHUB_WORKSPACE}/scripts/releaseWorkflowContract.mjs`
);
const { data: commit } = await github.rest.git.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: process.env.SOURCE_SHA,
});
const testRelease = resolveReleaseTestMode(process.env.TEST_TREE, commit.tree.sha);
core.setOutput('test-release', String(testRelease));
core.info(`Release source tree ${commit.tree.sha}; draft-only test: ${testRelease}`);

- name: Download Windows artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
Expand Down Expand Up @@ -369,6 +389,7 @@ jobs:
CHECKSUM_SHA256: ${{ steps.assets.outputs.checksum_sha256 }}
SOURCE_SHA: ${{ needs.determine.outputs.source-sha }}
TAG: ${{ needs.determine.outputs.tag }}
TEST_RELEASE: ${{ steps.mode.outputs.test-release }}
with:
script: |
const tag = process.env.TAG;
Expand Down Expand Up @@ -415,6 +436,14 @@ jobs:
throw new Error(`${tag} was missing an expected release asset`);
}

if (!['true', 'false'].includes(process.env.TEST_RELEASE)) {
throw new Error('Release test mode was not resolved');
}
if (process.env.TEST_RELEASE === 'true') {
core.info(`Verified ${tag} draft metadata; test releases are never published.`);
return;
}

let tagRef;
try {
tagRef = await github.rest.git.getRef({
Expand Down Expand Up @@ -455,6 +484,7 @@ jobs:
});

- name: Verify published release
if: steps.mode.outputs.test-release == 'false'
shell: bash
env:
ASSET_NAME: ${{ steps.assets.outputs.asset_name }}
Expand Down Expand Up @@ -492,3 +522,53 @@ jobs:
)
release_url="$(gh release view "$TAG" --json url --jq .url)"
echo "Published and verified [$TAG]($release_url)." >> "$GITHUB_STEP_SUMMARY"

- name: Verify test draft assets
if: steps.mode.outputs.test-release == 'true'
shell: bash
env:
ASSET_NAME: ${{ steps.assets.outputs.asset_name }}
ARCHIVE_SHA256: ${{ steps.assets.outputs.archive_sha256 }}
CHECKSUM_SHA256: ${{ steps.assets.outputs.checksum_sha256 }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_ID: ${{ steps.publish.outputs.id }}
SOURCE_SHA: ${{ needs.determine.outputs.source-sha }}
TAG: ${{ needs.determine.outputs.tag }}
run: |
set -euo pipefail
release_json="$(gh api "repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID")"
test "$(jq -r .draft <<<"$release_json")" = 'true'
test "$(jq -r .tag_name <<<"$release_json")" = "$TAG"
test "$(jq -r .target_commitish <<<"$release_json")" = "$SOURCE_SHA"
test "$(gh api "repos/$GITHUB_REPOSITORY/releases/latest" --jq .tag_name)" != "$TAG"
verify_dir="$(mktemp -d)"
gh release download "$TAG" --pattern "$ASSET_NAME" --pattern "$ASSET_NAME.sha256" --dir "$verify_dir"
(
cd "$verify_dir"
sha256sum --check "$ASSET_NAME.sha256"
unzip -tqq "$ASSET_NAME"
test "$(unzip -Z1 "$ASSET_NAME")" = 'Relay.exe'
test "$(sha256sum "$ASSET_NAME" | cut -d' ' -f1)" = "$ARCHIVE_SHA256"
test "$(sha256sum "$ASSET_NAME.sha256" | cut -d' ' -f1)" = "$CHECKSUM_SHA256"
)
echo "Verified draft-only test release $TAG; never published to the updater." >> "$GITHUB_STEP_SUMMARY"

- name: Remove test draft release
if: always() && steps.mode.outputs.test-release == 'true'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
SOURCE_SHA: ${{ needs.determine.outputs.source-sha }}
TAG: ${{ needs.determine.outputs.tag }}
with:
script: |
const { deleteTestDraft } = await import(
`${process.env.GITHUB_WORKSPACE}/scripts/releaseWorkflowContract.mjs`
);
await deleteTestDraft({
github,
owner: context.repo.owner,
repo: context.repo.repo,
tag: process.env.TAG,
sourceSha: process.env.SOURCE_SHA,
});
core.info(`Test draft ${process.env.TAG} is absent.`);
Loading
Loading