diff --git a/.github/ci.env b/.github/ci.env new file mode 100644 index 00000000..ee2c4d08 --- /dev/null +++ b/.github/ci.env @@ -0,0 +1 @@ +MAX_PR_COMMITS=100 diff --git a/.github/scripts/pr_comment.js b/.github/scripts/pr_comment.js index fd32ec0b..cde3281a 100644 --- a/.github/scripts/pr_comment.js +++ b/.github/scripts/pr_comment.js @@ -6,9 +6,13 @@ // @ts-check -const { getMergeableState, listOpenPulls } = require("./util"); +const { getPullDetail, listOpenPulls } = require("./util"); const COMMENT_TAG = ""; +const MAX_COMMITS = Number.parseInt(process.env.MAX_PR_COMMITS, 10); +if (!Number.isFinite(MAX_COMMITS) || MAX_COMMITS <= 0) { + throw new Error(`MAX_PR_COMMITS must be a positive integer (got: ${process.env.MAX_PR_COMMITS})`); +} /** * @param {{ github: import("@actions/github").getOctokit, context: import("@actions/github").context }} params @@ -53,8 +57,8 @@ async function buildFileCache({ github, owner, repo, pulls }) { * @returns {Promise} */ async function buildComment({ github, owner, repo, pr, pulls, fileCache }) { - const mergeableState = await getMergeableState({ github, owner, repo, prNumber: pr.number }); - const isDirty = mergeableState === "dirty"; + const detail = await getPullDetail({ github, owner, repo, prNumber: pr.number }); + const isDirty = detail.mergeable_state === "dirty"; const ourFiles = new Set(fileCache.get(pr.number) || []); const potentialConflicts = []; @@ -70,14 +74,21 @@ async function buildComment({ github, owner, repo, pr, pulls, fileCache }) { } } - let banner; + let banner = ""; + if (detail.commits > MAX_COMMITS) { + banner += + "> [!CAUTION]\n" + + `> This pull request has more than ${MAX_COMMITS} commits. Large pull requests are difficult to review. Consider splitting\n` + + "> your changes into smaller pull requests. Some Action runners will terminate until this is resolved.\n\n"; + } + if (isDirty) { - banner = "> [!CAUTION]\n> This pull request conflicts with the base branch. Please rebase and force-push."; + banner += "> [!CAUTION]\n> This pull request conflicts with the base branch. Please rebase and force-push."; } else if (potentialConflicts.length > 0) { - banner = + banner += "> [!WARNING]\n> This pull request may have conflicts, please coordinate with the authors of these pull requests."; } else { - banner = "> [!NOTE]\n> This pull request has no conflicts! 🎊 🎉 🎊"; + banner += "> [!NOTE]\n> This pull request has no conflicts! 🎊 🎉 🎊"; } let conflictSection = ""; diff --git a/.github/scripts/pr_tag.js b/.github/scripts/pr_tag.js index 007a01af..3334479a 100644 --- a/.github/scripts/pr_tag.js +++ b/.github/scripts/pr_tag.js @@ -6,7 +6,7 @@ // @ts-check -const { getMergeableState, listOpenPulls } = require("./util"); +const { getPullDetail, listOpenPulls } = require("./util"); const STALE_DAYS = 60; @@ -56,13 +56,13 @@ module.exports = async ({ github, context }) => { console.log(`PR #${pr.number}: removed stale`); } - const mergeableState = await getMergeableState({ github, owner, repo, prNumber: pr.number }); + const detail = await getPullDetail({ github, owner, repo, prNumber: pr.number }); - if (mergeableState === "unknown") { + if (detail.mergeable_state === "unknown") { continue; } - const hasConflicts = mergeableState === "dirty"; + const hasConflicts = detail.mergeable_state === "dirty"; const hasRebaseLabel = labels.includes(Labels.NEEDS_REBASE); if (hasConflicts && !hasRebaseLabel) { diff --git a/.github/scripts/util.js b/.github/scripts/util.js index f27b852f..1f412e01 100644 --- a/.github/scripts/util.js +++ b/.github/scripts/util.js @@ -37,21 +37,23 @@ async function listOpenPulls({ github, owner, repo }) { /** * @param {{ github: any, owner: string, repo: string, prNumber: number }} params - * @returns {Promise} + * @returns {Promise<{ mergeable_state: string, commits: number }>} */ -async function getMergeableState({ github, owner, repo, prNumber }) { +async function getPullDetail({ github, owner, repo, prNumber }) { + let commits = 0; for (let i = 0; i < MERGEABLE_RETRIES; i++) { const { data: detail } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber, }); + commits = detail.commits; if (detail.mergeable_state !== "unknown") { - return detail.mergeable_state; + return { mergeable_state: detail.mergeable_state, commits }; } await sleep(MERGEABLE_DELAY_MS); } - return "unknown"; + return { mergeable_state: "unknown", commits }; } -module.exports = { BASE_BRANCH, getMergeableState, listOpenPulls, sleep }; +module.exports = { BASE_BRANCH, getPullDetail, listOpenPulls, sleep }; diff --git a/.github/workflows/build_msrv.yml b/.github/workflows/build_msrv.yml index 17cc1110..b6f6222a 100644 --- a/.github/workflows/build_msrv.yml +++ b/.github/workflows/build_msrv.yml @@ -21,7 +21,18 @@ jobs: - name: Checkout uses: actions/checkout@v6 with: - fetch-depth: 0 + fetch-depth: 100 + + - name: Run pull request checks + if: github.event_name == 'pull_request' + shell: bash + run: | + source .github/ci.env + commits=${{ github.event.pull_request.commits }} + if [ "${commits}" -gt "${MAX_PR_COMMITS}" ]; then + echo "::error::PR has ${commits} commits (limit: ${MAX_PR_COMMITS})" + exit 1 + fi - name: Install Rust toolchain uses: dtolnay/rust-toolchain@1.85.0 @@ -57,27 +68,23 @@ jobs: key: cargo-deps-${{ hashFiles('Cargo.lock') }} restore-keys: cargo-deps- - - name: Restore CodeQL caches - id: codeql-cache + - name: Restore build artifacts uses: actions/cache/restore@v5 with: - path: | - contrib/codeql/.cache - ~/.codeql - key: codeql-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pkgs/**/*.rs', 'contrib/codeql/**/*.ql', 'contrib/codeql/**/*.qll', 'Cargo.lock') }} + path: target + key: cargo-build-msrv-${{ runner.os }}-${{ runner.arch }}-${{ github.sha }} + restore-keys: | + cargo-build-msrv-${{ runner.os }}-${{ runner.arch }}- + + - name: Manage CodeQL packs + uses: actions/cache@v5 + with: + path: ~/.codeql + key: codeql-packs-${{ hashFiles('contrib/codeql/codeql-pack.lock.yml') }} - name: Run linters run: python3 contrib/lint/all_lint.py - - name: Save CodeQL caches - if: success() && steps.codeql-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v5 - with: - path: | - contrib/codeql/.cache - ~/.codeql - key: codeql-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pkgs/**/*.rs', 'contrib/codeql/**/*.ql', 'contrib/codeql/**/*.qll', 'Cargo.lock') }} - - name: Check PR commit messages if: github.event_name == 'pull_request' run: > @@ -86,11 +93,24 @@ jobs: build: name: Build and test - runs-on: ubuntu-24.04 + runs-on: ubuntu-24.04-arm steps: - name: Checkout uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Run pull request checks + if: github.event_name == 'pull_request' + shell: bash + run: | + source .github/ci.env + commits=${{ github.event.pull_request.commits }} + if [ "${commits}" -gt "${MAX_PR_COMMITS}" ]; then + echo "::error::PR has ${commits} commits (limit: ${MAX_PR_COMMITS})" + exit 1 + fi - name: Install Rust toolchain uses: dtolnay/rust-toolchain@1.85.0 @@ -104,6 +124,14 @@ jobs: key: cargo-deps-${{ hashFiles('Cargo.lock') }} restore-keys: cargo-deps- + - name: Manage build artifacts + uses: actions/cache@v5 + with: + path: target + key: cargo-build-msrv-${{ runner.os }}-${{ runner.arch }}-${{ github.sha }} + restore-keys: | + cargo-build-msrv-${{ runner.os }}-${{ runner.arch }}- + - name: Build workspace run: cargo build --workspace --features full,_internal diff --git a/.github/workflows/build_nightly.yml b/.github/workflows/build_nightly.yml index e7d2b43d..14e443fc 100644 --- a/.github/workflows/build_nightly.yml +++ b/.github/workflows/build_nightly.yml @@ -34,6 +34,17 @@ jobs: with: fetch-depth: 1 + - name: Run pull request checks + if: github.event_name == 'pull_request' + shell: bash + run: | + source .github/ci.env + commits=${{ github.event.pull_request.commits }} + if [ "${commits}" -gt "${MAX_PR_COMMITS}" ]; then + echo "::error::PR has ${commits} commits (limit: ${MAX_PR_COMMITS})" + exit 1 + fi + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master with: @@ -43,8 +54,8 @@ jobs: - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - - name: Restore cargo registry - uses: actions/cache/restore@v5 + - name: Manage cargo registry + uses: actions/cache@v5 with: path: | ~/.cargo/registry @@ -52,7 +63,7 @@ jobs: key: cargo-deps-${{ hashFiles('Cargo.lock') }} restore-keys: cargo-deps- - - name: Restore build artifacts + - name: Manage build artifacts uses: actions/cache@v5 with: path: target @@ -88,12 +99,3 @@ jobs: - name: Sanity check benchmarks run: cargo bench -p ${{ inputs.package }} --features ${{ inputs.features }} --no-run - - - name: Save cargo registry - if: always() - uses: actions/cache/save@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: cargo-deps-${{ hashFiles('Cargo.lock') }} diff --git a/.github/workflows/build_stable.yml b/.github/workflows/build_stable.yml index 08f56b41..85f1f59f 100644 --- a/.github/workflows/build_stable.yml +++ b/.github/workflows/build_stable.yml @@ -42,11 +42,22 @@ jobs: with: fetch-depth: 1 + - name: Run pull request checks + if: github.event_name == 'pull_request' + shell: bash + run: | + source .github/ci.env + commits=${{ github.event.pull_request.commits }} + if [ "${commits}" -gt "${MAX_PR_COMMITS}" ]; then + echo "::error::PR has ${commits} commits (limit: ${MAX_PR_COMMITS})" + exit 1 + fi + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable - - name: Restore cargo registry - uses: actions/cache/restore@v5 + - name: Manage cargo registry + uses: actions/cache@v5 with: path: | ~/.cargo/registry @@ -54,7 +65,7 @@ jobs: key: cargo-deps-${{ hashFiles('Cargo.lock') }} restore-keys: cargo-deps- - - name: Restore build artifacts + - name: Manage build artifacts uses: actions/cache@v5 with: path: target @@ -67,12 +78,3 @@ jobs: - name: Test package run: cargo test -p ${{ inputs.package }} --features ${{ inputs.features }} - - - name: Save cargo registry - if: always() - uses: actions/cache/save@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: cargo-deps-${{ hashFiles('Cargo.lock') }} diff --git a/.github/workflows/pr_comment.yml b/.github/workflows/pr_comment.yml index c2ca63d7..f6a86610 100644 --- a/.github/workflows/pr_comment.yml +++ b/.github/workflows/pr_comment.yml @@ -1,10 +1,10 @@ name: Comment on PRs on: - push: - branches: [develop] pull_request_target: types: [synchronize, opened, reopened, closed] + schedule: + - cron: "0 6 * * 1" concurrency: group: ${{ github.workflow }} @@ -24,6 +24,9 @@ jobs: with: fetch-depth: 0 + - name: Read environment + run: cat .github/ci.env >> "$GITHUB_ENV" + - name: Update PR comments uses: actions/github-script@v8 with: diff --git a/.github/workflows/pr_tag.yml b/.github/workflows/pr_tag.yml index c8965c58..bdddedb2 100644 --- a/.github/workflows/pr_tag.yml +++ b/.github/workflows/pr_tag.yml @@ -1,10 +1,8 @@ name: Tag PRs on: - push: - branches: [develop] pull_request_target: - types: [synchronize, opened, reopened] + types: [synchronize, opened, reopened, closed] schedule: - cron: "0 6 * * 1" diff --git a/codecov.yml b/codecov.yml index 828300e1..3d0dcdba 100644 --- a/codecov.yml +++ b/codecov.yml @@ -6,23 +6,26 @@ coverage: dash-num: flags: [dash-num] target: auto - dash-script: - flags: [dash-script] + dash-p2p-core: + flags: [dash-p2p-core] target: auto - dash-pow: - flags: [dash-pow] + dash-params: + flags: [dash-params] target: auto dash-pkc: flags: [dash-pkc] target: auto + dash-pow: + flags: [dash-pow] + target: auto dash-primitives: flags: [dash-primitives] target: auto - dash-params: - flags: [dash-params] + dash-script: + flags: [dash-script] target: auto - dash-p2p-core: - flags: [dash-p2p-core] + dash-types: + flags: [dash-types] target: auto patch: default: @@ -30,23 +33,26 @@ coverage: dash-num: flags: [dash-num] target: auto - dash-script: - flags: [dash-script] + dash-p2p-core: + flags: [dash-p2p-core] target: auto - dash-pow: - flags: [dash-pow] + dash-params: + flags: [dash-params] target: auto dash-pkc: flags: [dash-pkc] target: auto + dash-pow: + flags: [dash-pow] + target: auto dash-primitives: flags: [dash-primitives] target: auto - dash-params: - flags: [dash-params] + dash-script: + flags: [dash-script] target: auto - dash-p2p-core: - flags: [dash-p2p-core] + dash-types: + flags: [dash-types] target: auto flags: @@ -56,18 +62,21 @@ flags: dash-script: paths: [pkgs/script/] carryforward: true - dash-pow: - paths: [pkgs/pow/] + dash-p2p-core: + paths: [pkgs/p2p_core/] + carryforward: true + dash-params: + paths: [pkgs/params/] carryforward: true dash-pkc: paths: [pkgs/pkc/] carryforward: true + dash-pow: + paths: [pkgs/pow/] + carryforward: true dash-primitives: paths: [pkgs/primitives/] carryforward: true - dash-params: - paths: [pkgs/params/] - carryforward: true - dash-p2p-core: - paths: [pkgs/p2p_core/] + dash-types: + paths: [pkgs/types/] carryforward: true diff --git a/contrib/js/eslint.config.mjs b/contrib/js/eslint.config.mjs index 815e4bc5..37d75442 100644 --- a/contrib/js/eslint.config.mjs +++ b/contrib/js/eslint.config.mjs @@ -8,6 +8,7 @@ export default [ globals: { console: "readonly", module: "readonly", + process: "readonly", require: "readonly", setTimeout: "readonly", },