Render message-route anchor before surrounding context loads #588
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [master, develop] | |
| tags: | |
| - "[0-9]+.[0-9]+.[0-9]+*" | |
| pull_request: | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| check: | |
| name: Lint, Typecheck, Test | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .nvmrc | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm install | |
| - name: TypeScript | |
| run: npm run typecheck | |
| - name: ESLint | |
| run: npm run lint | |
| # - name: Prettier | |
| # run: npm run format:check | |
| # | |
| # - name: Cognitive complexity | |
| # run: npm run lint:cc | |
| # | |
| # - name: Security audit | |
| # run: npm run audit:high --workspace=web | |
| - name: Tests | |
| run: npm run test | |
| - name: Knip (dead code report) | |
| continue-on-error: true | |
| run: npm run knip --workspace=web | |
| - name: Build (web) | |
| run: | | |
| echo "${PROD_ENVS}" | base64 -d > packages/web/.env.production | |
| npm run build --workspace=web | |
| env: | |
| VITE_APP_VERSION: ${{ github.sha }} | |
| PROD_ENVS: ${{ secrets.WEB_PROD_ENVS }} | |
| e2e: | |
| name: E2E (Playwright) | |
| needs: check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .nvmrc | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Install Playwright Chromium | |
| run: npx playwright install --with-deps chromium | |
| - name: Run E2E tests | |
| run: npm run e2e -- --project=chromium --grep-invert @live | |
| env: | |
| CI: true | |
| - name: Upload Playwright report | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-report | |
| path: | | |
| e2e/results | |
| playwright-report | |
| retention-days: 7 | |
| build-electron: | |
| name: Build Electron (${{ matrix.os }}) | |
| needs: check | |
| if: > | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'push' && | |
| (github.ref == 'refs/heads/master' || github.ref_type == 'tag')) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| cmd: package:electron:linux -- --linux rpm deb AppImage | |
| - os: windows-latest | |
| cmd: package:electron:win | |
| - os: macos-latest | |
| cmd: package:electron:mac | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .nvmrc | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm install | |
| # - name: Install Electron dependencies | |
| # run: npm ci | |
| # working-directory: packages/electron | |
| # macOS: import signing certificate from secrets | |
| # - name: Import macOS signing certificate | |
| # if: matrix.os == 'macos-latest' && env.CSC_LINK != '' | |
| # env: | |
| # CSC_LINK: ${{ secrets.MAC_CSC_LINK }} | |
| # CSC_KEY_PASSWORD: ${{ secrets.MAC_CSC_KEY_PASSWORD }} | |
| # run: echo "Certificate imported from secrets" | |
| - name: Build web | |
| shell: bash | |
| run: | | |
| echo "${PROD_ENVS}" | base64 -d > packages/web/.env.production | |
| npm run build --workspace=web | |
| env: | |
| VITE_APP_VERSION: ${{ github.sha }} | |
| PROD_ENVS: ${{ secrets.WEB_PROD_ENVS }} | |
| - name: Build & Package | |
| run: | | |
| echo "${PROD_ENVS}" | base64 -d > packages/web/.env.production | |
| npm run ${{ matrix.cmd }} | |
| env: | |
| PROD_ENVS: ${{ secrets.WEB_PROD_ENVS }} | |
| # # macOS code signing (set secrets in GitHub repo settings) | |
| # CSC_LINK: ${{ secrets.MAC_CSC_LINK }} | |
| # CSC_KEY_PASSWORD: ${{ secrets.MAC_CSC_KEY_PASSWORD }} | |
| # # macOS notarization | |
| # APPLE_ID: ${{ secrets.APPLE_ID }} | |
| # APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} | |
| # APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| # # Windows code signing (future) | |
| # # CSC_LINK: ${{ secrets.WIN_CSC_LINK }} | |
| # # CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }} | |
| - name: Collect release files | |
| id: collect | |
| shell: bash | |
| run: | | |
| mkdir -p upload | |
| cp "packages/electron/release"/Exordos\ Workspace* upload/ 2>/dev/null || true | |
| ls -la upload/ | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: electron-${{ matrix.os }} | |
| path: upload/* | |
| retention-days: 14 | |
| publish-apt: | |
| name: Publish apt repository | |
| needs: build-electron | |
| # Self-hosted: the repository host is reached from inside the network, the same | |
| # way the Exordos element is published. A GitHub-hosted runner depends on port 22 | |
| # staying open to the internet. | |
| runs-on: [self-hosted, vm] | |
| if: github.ref_type == 'tag' | |
| timeout-minutes: 10 | |
| steps: | |
| # This runner keeps its workspace between runs. Leftovers from an earlier | |
| # release would be uploaded again, which can resurrect a version that | |
| # retention already dropped on the host. | |
| - name: Reset workspace | |
| run: rm -rf artifacts debs | |
| - name: Download Linux artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: electron-ubuntu-latest | |
| path: artifacts | |
| # Prereleases stay out of the repository: in a deb version everything after | |
| # the last hyphen is the revision, so 0.4.0-rc1 sorts ABOVE 0.4.0 and apt | |
| # would offer the release candidate as the newest stable build. | |
| - name: Select packages | |
| id: select | |
| run: | | |
| set -euo pipefail | |
| if [[ ! "${GITHUB_REF_NAME}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "::warning::${GITHUB_REF_NAME} is a prerelease tag — not published to the apt repository" | |
| echo "publish=false" >> "${GITHUB_OUTPUT}" | |
| exit 0 | |
| fi | |
| mkdir -p debs | |
| find artifacts -maxdepth 1 -name '*.deb' -exec cp {} debs/ \; | |
| count=$(find debs -name '*.deb' | wc -l) | |
| echo "Packages to publish: ${count}" | |
| ls -la debs/ | |
| if [[ "${count}" -eq 0 ]]; then | |
| echo "::error::no .deb artifacts found for tag ${GITHUB_REF_NAME}" | |
| exit 1 | |
| fi | |
| echo "publish=true" >> "${GITHUB_OUTPUT}" | |
| # The repository host holds the pool, the aptly database and the signing | |
| # key; this job only hands it the new packages. The deploy key is bound to | |
| # a forced command there, and to this project's package names, so it cannot | |
| # do anything else. | |
| - name: Upload to repository host | |
| if: steps.select.outputs.publish == 'true' | |
| env: | |
| SSH_TARGET: ${{ secrets.APT_REPO_SSH_TARGET }} | |
| SSH_KEY: ${{ secrets.APT_REPO_SSH_KEY }} | |
| SSH_KNOWN_HOSTS: ${{ secrets.APT_REPO_SSH_KNOWN_HOSTS }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -z "${SSH_TARGET}" ]]; then | |
| echo "::warning::APT_REPO_SSH_TARGET is not configured — skipping apt publication" | |
| exit 0 | |
| fi | |
| # Credentials go to RUNNER_TEMP, not the workspace: the runner wipes it | |
| # per job, while the workspace outlives the run. | |
| umask 077 | |
| printf '%s\n' "${SSH_KEY}" > "${RUNNER_TEMP}/deploy_key" | |
| printf '%s\n' "${SSH_KNOWN_HOSTS}" > "${RUNNER_TEMP}/known_hosts" | |
| # Only the .deb files, listed explicitly: the repository host rejects an | |
| # upload carrying anything else, and `release` now waits for that upload, | |
| # so a stray file in this directory would block the whole release. | |
| find debs -maxdepth 1 -name '*.deb' -printf '%P\0' \ | |
| | tar -C debs --null --files-from=- -c \ | |
| | ssh -T \ | |
| -i "${RUNNER_TEMP}/deploy_key" \ | |
| -o IdentitiesOnly=yes \ | |
| -o UserKnownHostsFile="${RUNNER_TEMP}/known_hosts" \ | |
| -o StrictHostKeyChecking=yes \ | |
| "${SSH_TARGET}" | |
| - name: Clean up credentials | |
| if: always() | |
| run: rm -f "${RUNNER_TEMP}/deploy_key" "${RUNNER_TEMP}/known_hosts" | |
| release: | |
| name: Create GitHub Release | |
| # Waits for the apt publication as well: announcing a release that never reached | |
| # the repository leaves `apt upgrade` users without the version the release notes | |
| # promise. The remaining ordering risk is the mirror image — packages published | |
| # while this job fails — which is recoverable by re-running it. | |
| needs: [build-electron, publish-apt] | |
| runs-on: ubuntu-latest | |
| if: github.ref_type == 'tag' | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| pattern: electron-* | |
| merge-multiple: true | |
| - name: List artifacts | |
| run: ls -la artifacts/ | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: artifacts/Exordos Workspace* | |
| generate_release_notes: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |