Nightly Build #306
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: Nightly Build | |
| on: | |
| schedule: | |
| - cron: "0 0 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| logseq_branch: | |
| description: "Logseq branch to build" | |
| required: false | |
| default: master | |
| publish_release: | |
| description: "Publish release and commit manifest update" | |
| required: true | |
| type: boolean | |
| default: true | |
| # Least-privilege default for jobs without their own block; writing jobs | |
| # elevate per-job. The reusable build call needs only contents: read. | |
| permissions: | |
| contents: read | |
| jobs: | |
| build: | |
| uses: ./.github/workflows/build-desktop.yml | |
| with: | |
| logseq_branch: ${{ github.event.inputs.logseq_branch || 'master' }} | |
| publish-release: | |
| if: github.event_name == 'schedule' || inputs.publish_release == true | |
| runs-on: ubuntu-24.04 | |
| needs: build | |
| permissions: | |
| contents: write # push the manifest bump and publish the release | |
| statuses: write # attach test-build-required to the bump commit | |
| concurrency: | |
| group: repo-main-writer | |
| cancel-in-progress: false | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| - name: Download packaged builds | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh run download "${{ github.run_id }}" --repo "${{ github.repository }}" --dir builds | |
| - name: Resolve build metadata | |
| env: | |
| REPO: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| meta="$(find builds -name meta.txt | head -1)" | |
| x64_tar="$(find builds -name 'logseq-linux-x64-*.tar.gz' | head -1)" | |
| arm64_tar="$(find builds -name 'logseq-linux-arm64-*.tar.gz' | head -1)" | |
| darwin_arm64_tar="$(find builds -name 'logseq-darwin-arm64-*.tar.gz' | head -1)" | |
| notes="$(find builds -name release-notes.md | head -1)" | |
| x64_hash_file="$(find builds -name 'hash-x64.txt' | head -1)" | |
| arm64_hash_file="$(find builds -name 'hash-arm64.txt' | head -1)" | |
| darwin_arm64_hash_file="$(find builds -name 'hash-darwin-arm64.txt' | head -1)" | |
| for f in "$meta" "$x64_tar" "$arm64_tar" "$darwin_arm64_tar" "$notes" "$x64_hash_file" "$arm64_hash_file" "$darwin_arm64_hash_file"; do | |
| test -n "$f" || { echo "Required artifact file missing" >&2; exit 1; } | |
| done | |
| # meta.txt holds trusted build-step outputs (version/revision/datestring). | |
| version="$(grep -E '^version=' "$meta" | cut -d= -f2-)" | |
| revision="$(grep -E '^revision=' "$meta" | cut -d= -f2-)" | |
| datestring="$(grep -E '^datestring=' "$meta" | cut -d= -f2-)" | |
| tag="nightly-${datestring}" | |
| darwin_arm64_asset_url="https://github.com/${REPO}/releases/download/${tag}/$(basename "$darwin_arm64_tar")" | |
| darwin_arm64_asset_sha256="$(cat "$darwin_arm64_hash_file")" | |
| { | |
| echo "LOGSEQ_VERSION=${version}" | |
| echo "LOGSEQ_REV=${revision}" | |
| echo "NIGHTLY_TAG=${tag}" | |
| echo "RELEASE_TAG=${tag}" | |
| echo "BUILD_REVISION=${revision}" | |
| echo "X64_TARBALL=${x64_tar}" | |
| echo "ARM64_TARBALL=${arm64_tar}" | |
| echo "DARWIN_ARM64_TARBALL=${darwin_arm64_tar}" | |
| echo "RELEASE_NOTES=${notes}" | |
| echo "ASSET_URL_X86_64=https://github.com/${REPO}/releases/download/${tag}/$(basename "$x64_tar")" | |
| echo "ASSET_SHA256_X86_64=$(cat "$x64_hash_file")" | |
| echo "ASSET_URL_AARCH64=https://github.com/${REPO}/releases/download/${tag}/$(basename "$arm64_tar")" | |
| echo "ASSET_SHA256_AARCH64=$(cat "$arm64_hash_file")" | |
| echo "ASSET_URL_AARCH64_DARWIN=${darwin_arm64_asset_url}" | |
| echo "ASSET_SHA256_AARCH64_DARWIN=${darwin_arm64_asset_sha256}" | |
| } >>"$GITHUB_ENV" | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@v31 | |
| with: | |
| extra_nix_config: | | |
| accept-flake-config = true | |
| - name: Setup Cachix | |
| uses: cachix/cachix-action@v17 | |
| with: | |
| name: nix-logseq-git-flake | |
| authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} | |
| - name: Publish GitHub release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| rev_short="${BUILD_REVISION:0:7}" | |
| release_assets=("$X64_TARBALL" "$ARM64_TARBALL" "$DARWIN_ARM64_TARBALL") | |
| gh release delete "$RELEASE_TAG" -y || true | |
| gh release create "$RELEASE_TAG" "${release_assets[@]}" \ | |
| --title "logseq-nightly-${rev_short}" \ | |
| --notes-file "$RELEASE_NOTES" \ | |
| --target main | |
| - name: Update nightly manifest and CLI hashes | |
| run: bash scripts/update-nightly.sh | |
| # Realize the architecture-independent CLI FODs with their freshly | |
| # resolved hashes so Cachix uploads them; aarch64 jobs substitute these | |
| # paths instead of rebuilding them. | |
| - name: Seed CLI fixed-output derivations to Cachix | |
| run: | | |
| nix build --accept-flake-config --print-build-logs \ | |
| .#logseq-cli.cliCljDeps \ | |
| .#logseq-cli.cliPnpmDeps | |
| - name: Configure git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| - name: Format repository | |
| run: nix fmt | |
| - name: Validate flake | |
| run: | | |
| nix flake check | |
| - name: Commit nightly update | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| if git diff --quiet -- data/logseq-nightly.json; then | |
| echo "Manifest unchanged" | |
| exit 0 | |
| fi | |
| git add data/logseq-nightly.json | |
| git commit -F - <<EOF | |
| chore: bump nightly manifest | |
| The nightly publish-release job rewrites data/logseq-nightly.json for ${NIGHTLY_TAG}. | |
| It records upstream revision ${LOGSEQ_REV} and refreshed CLI fixed-output hashes. | |
| Validation: nix fmt | |
| Validation: nix flake check | |
| EOF | |
| repo_url="https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" | |
| git fetch "$repo_url" main | |
| git rebase FETCH_HEAD | |
| commit_sha="$(git rev-parse HEAD)" | |
| staging_branch="automation/nightly-manifest-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" | |
| cleanup_staging_branch() { | |
| # Best-effort: a leaked automation/nightly-manifest-* ref is | |
| # harmless, but a failed delete should be visible in the log. | |
| git push "$repo_url" ":refs/heads/${staging_branch}" \ | |
| || echo "WARNING: could not delete staging ref ${staging_branch}" >&2 | |
| } | |
| trap cleanup_staging_branch EXIT | |
| # Branch protection requires test-build-required before the commit | |
| # reaches main. Publish the commit object on a short-lived ref, then | |
| # attach the status after the real nightly validation has passed. | |
| git push "$repo_url" "HEAD:refs/heads/${staging_branch}" | |
| gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${commit_sha}" \ | |
| -f state=success \ | |
| -f context="test-build-required" \ | |
| -f description="Nightly publish-release validated this manifest bump." \ | |
| -f target_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| git push "$repo_url" HEAD:main | |
| report-failure: | |
| if: ${{ always() && github.event_name == 'schedule' && (needs.build.result != 'success' || needs['publish-release'].result != 'success') }} | |
| needs: | |
| - build | |
| - publish-release | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| issues: write | |
| steps: | |
| - name: Create or update failure issue | |
| uses: actions/github-script@v9 | |
| env: | |
| ISSUE_TITLE: Nightly build automation failing | |
| ISSUE_MARKER: <!-- nightly-build-failure --> | |
| FAILURE_LABELS: type(bug),area(ci),area(automation),area(nightly),status(needs-manual-review),priority(p2),origin(automated) | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| BUILD_RESULT: ${{ needs.build.result }} | |
| PUBLISH_RESULT: ${{ needs['publish-release'].result }} | |
| with: | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const title = process.env.ISSUE_TITLE; | |
| const marker = process.env.ISSUE_MARKER; | |
| const failureLabels = process.env.FAILURE_LABELS.split(",").filter(Boolean); | |
| const runUrl = process.env.RUN_URL; | |
| const buildResult = process.env.BUILD_RESULT; | |
| const publishResult = process.env.PUBLISH_RESULT; | |
| const timestamp = new Date().toISOString(); | |
| const body = [ | |
| marker, | |
| "Nightly build workflow failed on a scheduled run.", | |
| "", | |
| `Latest failed run: ${runUrl}`, | |
| "", | |
| "This is a rolling issue; new failures are added as comments." | |
| ].join("\n"); | |
| const openIssues = await github.paginate(github.rest.issues.listForRepo, { | |
| owner, | |
| repo, | |
| state: "open", | |
| per_page: 100 | |
| }); | |
| const existing = openIssues.find( | |
| (issue) => | |
| !issue.pull_request && | |
| issue.title === title && | |
| issue.body && | |
| issue.body.includes(marker) | |
| ); | |
| const commentBody = [ | |
| `Failure detected at ${timestamp}.`, | |
| `Run: ${runUrl}`, | |
| `build=${buildResult}, publish-release=${publishResult}` | |
| ].join("\n"); | |
| if (existing) { | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number: existing.number, | |
| body: commentBody | |
| }); | |
| await github.rest.issues.addLabels({ | |
| owner, | |
| repo, | |
| issue_number: existing.number, | |
| labels: failureLabels | |
| }); | |
| core.info(`Updated existing issue #${existing.number}`); | |
| } else { | |
| const created = await github.rest.issues.create({ | |
| owner, | |
| repo, | |
| title, | |
| body, | |
| labels: failureLabels | |
| }); | |
| core.info(`Created issue #${created.data.number}`); | |
| } | |
| report-recovery: | |
| if: ${{ always() && github.event_name == 'schedule' && needs.build.result == 'success' && needs['publish-release'].result == 'success' }} | |
| needs: | |
| - build | |
| - publish-release | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| issues: write | |
| steps: | |
| - name: Close failure issue after recovery | |
| uses: actions/github-script@v9 | |
| env: | |
| ISSUE_TITLE: Nightly build automation failing | |
| ISSUE_MARKER: <!-- nightly-build-failure --> | |
| RECOVERED_LABEL: status(recovered) | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| with: | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const title = process.env.ISSUE_TITLE; | |
| const marker = process.env.ISSUE_MARKER; | |
| const recoveredLabel = process.env.RECOVERED_LABEL; | |
| const runUrl = process.env.RUN_URL; | |
| const timestamp = new Date().toISOString(); | |
| const openIssues = await github.paginate(github.rest.issues.listForRepo, { | |
| owner, | |
| repo, | |
| state: "open", | |
| per_page: 100 | |
| }); | |
| const existing = openIssues.find( | |
| (issue) => | |
| !issue.pull_request && | |
| issue.title === title && | |
| issue.body && | |
| issue.body.includes(marker) | |
| ); | |
| if (!existing) { | |
| core.info("No open rolling nightly failure issue to close."); | |
| return; | |
| } | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number: existing.number, | |
| body: [ | |
| `Recovery detected at ${timestamp}.`, | |
| `Successful run: ${runUrl}`, | |
| "Closing this rolling failure issue." | |
| ].join("\n") | |
| }); | |
| await github.rest.issues.removeLabel({ | |
| owner, | |
| repo, | |
| issue_number: existing.number, | |
| name: "status(needs-manual-review)" | |
| }).catch((error) => { | |
| if (error.status !== 404) { | |
| throw error; | |
| } | |
| }); | |
| await github.rest.issues.addLabels({ | |
| owner, | |
| repo, | |
| issue_number: existing.number, | |
| labels: [recoveredLabel] | |
| }); | |
| await github.rest.issues.update({ | |
| owner, | |
| repo, | |
| issue_number: existing.number, | |
| state: "closed" | |
| }); | |
| core.info(`Closed issue #${existing.number}`); |