Repository navigation
CI: size check for added files and release-hosted fits (#438) - #517
Merged
Merged
Conversation
…rticle-fits release (#438)
The check diffed BASE...HEAD, so a fit added in one commit and deleted in a later one passed, yet a merge commit carries the blob into develop for good. The step now lists the objects reachable from the PR head and from neither the base nor origin/develop, and fails on every blob above the limit, naming its path and the commit that added it. Blobs already in develop are exempt, so a dev-* PR whose head merged develop does not fail on grandfathered fits. The blobs the blobless clone lacks come in one fetch. Looked up one by one, each blob arrives in its own pack, about 0.4 s each, and the lookup aborts after about 50 packs.
… release (#438) When the article-fits release does not exist, the download step continues with no fits, and a fit that an article loads from fits/ but the release lacks is fitted during the site build. A new step lists every file = "fits/<name>" in vignettes/articles/*.Rmd after the download and fails, naming the article and the fit, when a listed fit is absent or readRDS() cannot read it. A fits/ path that is not a string literal passed to file fails too, because it cannot be listed.
…ase is created (#438) The workflow runs on release: published, which also fires for a pre-release, and under that event the job builds the commit of the tag and deploys it to gh-pages. The article-fits release only holds fits, so the job is skipped for that tag. Versioned releases deploy as before.
The section now says that the file-size check reads every commit of the PR, not only the final tree; that files already in develop do not count; and that the site build stops, naming the article and the fit, when a fit loaded from fits/ is missing from the release. It also asks for the path as a string literal, which the build needs to list the fits.
…ory; run on retarget (#438) An independent review found three gaps in the scan. - Excluding the base and develop with ^rev drops only the trees of their tips, so a PR that restores a fit that develop had deleted earlier failed although the blob is in the protected history. The step now removes every object reachable from the base or from develop. - A stacked PR is retargeted to develop when its base merges, which is an edited event, so the check never ran for it. edited is now a trigger type. - Paths with a comma, colon or percent sign broke the error annotation.
#438) An independent review found fits the step could not see and references it rejected for no reason. - Read every vignettes/**/*.[Rrq]md, as pkgdown does, and look for the fit next to the article. - Follow knitr's fences: chunks in blockquotes count, chunk syntax shown in a longer fence does not, chunks with eval = FALSE are skipped. - Fail on inline code that names a fits/ path. - A bare "fits" string counts only inside a file argument, so .key = "fits" no longer fails; `file` in backticks is recognised. - Invalid UTF-8 in an article no longer makes the step see nothing.
A merge commit keeps every commit of a PR, a squash merge does not; files held anywhere in the history of develop or of the base do not count; the site build reads file = "fits/<name>" in R chunks only.
cat-file prints '<oid> missing' for an object the batched fetch did not deliver, and the size loop skipped such a line, so an unsized candidate passed unchecked. The step now reports the count and fails. The annotation names the commit by its first ten hex digits (--no-abbrev also disabled %h), and the comment on the edited trigger says it fires for title and body edits.
The R code of the "Check article fits" step moves unchanged to .github/scripts/check-article-fits.R, so that it can be parsed, linted and run on fixtures with Rscript.
…ath (#438) The fit check's chunk scanner now applies knitr 1.52's rule: plain fences are text, a chunk header opens a chunk wherever it stands, only a fence with the header's own prefix and backtick count closes it, an unclosed chunk runs to the end of the file, and #| options are read from the leading lines only. A chunk shown inside a four-backtick block, a chunk after an unclosed plain fence, an unclosed chunk and #| eval: false after code were evaluated by knitr but passed the check. The check skips the files pkgdown does not build (names starting with _, vignettes/tutorials/), checks every literal relative file = path, so a fit in assets/ that is not in the tree fails too, ignores saveRDS(), save() and write_rds() calls, and escapes its annotations.
# Conflicts: # .github/CONTRIBUTING.md
…es/ is absent (#438) The fit check treated every literal file = as a fit and appended .rds, so read.csv(), write.csv(), png(), cat() and readRDS(file = "x.RDS") failed the build. It now checks only bmm(), brm(), update(), readRDS() and read_rds(), appends .rds only where bmm() and brm() do, ignores writers, and still fails on any fits/ path it cannot check. Without a vignettes/ folder it stops instead of reporting 0 problems.
This was referenced Oct 8, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the CI part of #438 (ADR 0003). No fit file is moved or removed here.
What the checks do
file-size(new; PRs intomaster,developanddev-*) fails a PR when any commit in it adds a blob above 1 MiB, even if a later commit deletes the file. A merge commit keeps every commit of the PR, so a blob that a PR adds and removes still lands in the history for good (a squash merge would not, but the check cannot know which one will be used). The step names the path and commit of every blob over the limit. A blob held anywhere in the history of the base or oforigin/developpasses, so renames, forward merges and restores of the fits tracked today (32 files, 150.6 MiB, invignettes/articles/assets/; 87.rdsblobs, 471.8 MiB, reachable from develop) do not trip it. It also runs onedited, which is what retargeting a stacked PR raises.The first version compared the PR tip with the base. A PR that added a 2 MB fit and deleted it in the next commit passed that scan. The new scan fails it.
pkgdown(changed):file =that an evaluated R chunk passes tobmm(),brm(),readRDS()orread_rds(), in the articles pkgdown builds (vignettes/**/*.[Rrq]mdwithout_-prefixed files andvignettes/tutorials/). Afits/file must be in the release, any other file must be tracked. It fails, naming the article and the file, when one is missing orreadRDS()cannot read it. Afits/path it cannot check fails too: a positionalreadRDS("fits/<name>"), a path built withpaste0()orfile.path(), afile =of any other function except a writer, and inline code that names one. With this step the no-release case (HTTP 404) stays a harmless no-op. No article loads fromfits/yet. On the current tree it checks 26 references in 16 articles, all inassets/, with 0 problems.article-fitstag. Creating that release firesrelease: published(a pre-release does too), and without the guard the job builds the tagged commit and deploys it to gh-pages.CONTRIBUTING states this scope.
Changes after review
.github/scripts/check-article-fits.R.```{r …}header line and#|options count only at the top of the chunk. Four shapes were missed before: a chunk shown inside a longer fenced block, a{r}header after an unclosed plain fence, a chunk that is never closed, and#| eval: falseafter the first line of code._-prefixed,vignettes/tutorials/) are not checked.file =passed tobmm(),brm(),readRDS()orread_rds()is checked,assets/...as well asfits/.... Afits/file must be in the release, any other file must be tracked..rdsis appended only wherebmm()andbrm()append it;readRDS()andread_rds()paths are checked as written.file =passed to a writer (saveRDS(),save(),write_rds(),write.csv(),write.table(),writeLines(),cat(),sink(),ggsave(),png(),jpeg(),pdf(),svg()) is ignored. Any otherfile =, such asread.csv(file = "assets/data.csv")orupdate(fit, file = "assets/x"), is ignored unless it names afits/path, which fails because it cannot be checked.update()is in neither list:update.bmmfit()only writesfile, butupdate()of abrm()fit reads it.vignettes/folder, so a changed working directory cannot turn it into a silent pass.assets/fits in evaluated chunks on the stale branch308-hypothesis-testing-article(assets/bmmfit_postestimation_{sdm,sdm_null,mix2p}). The old step looked only atfits/paths and did not see them.file-sizefails when an object cannot be fetched.The limit
I set it at 1 MiB. That settles the open line in ADR 0003, whose text is not edited here.
Branches that fail today
Measured against
origin/develop(c47e6f9) for the 57 remote branches other than develop and gh-pages:race-lnr(#501)bmmfit_lnr_hierarchical_vignette.rds, 3.3 MBfits/, and drop the commit that added it from the branch. A later delete does not help.352-log-normal-race-modelrace-lnr.mv-articlerace-lnr.reliabilitytests/testthat/assets/bmmfit_sdm_reliability.rds, 1.45 MBfeature/chkptstanrNot covered
actionlintis not installed. Thefile-sizeblock was run as text extracted from the YAML, and the fit check asRscript .github/scripts/check-article-fits.R.file-sizeis not a required check. Todaydeveloprequires only the threeR-CMD-checkjobs, so a redfile-sizeblocks nothing until it is added.file-size.yamlcan weaken its own check. Review of.github/is the guard. This is inferred from the docs and was not run.file =arguments tobmm(),brm(),readRDS()andread_rds()in evaluated R chunks. It does not see sourced code, child documents, or a directory held in a variable. It only reads the fit, so a readable fit from the wrong model passes.ifstill takes thepkgdownconcurrency slot.What was run
blob:noneclone of the repository, all as expected (16 of the real ranges also in the full local repo). It fails an add-then-delete, a large base file rewritten to a smaller large file, a large blob that exists only in a merge resolution, a blob on a merged side branch, and 1,048,577 bytes. It passes a rename, 1,048,576 bytes, adev-*base whose PR head merged a develop that holds a large fit, and a restore of a fit that develop or the base had deleted earlier. Controls: pointingorigin/developelsewhere makes thedev-*range fail.origin/developpinned to the PR base because the PR is merged) andorigin/race-model-core..origin/race-lnr. Real ranges that pass: Add multinomial processing tree models with mpt() (MPT 1/6, #255) #484 to MPT article, ppp(), mpt_data_reshape() and mpt() API changes (MPT 6/6, #255) #489, Add trial-to-trial variability in non-decision time (sndt) tocswald()(#387) #406, Add the shared infrastructure for the race models (lba, lnr, rdm) #499, Add the Linear Ballistic Accumulator (lba) on the shared race core (#349) #500, Add the Racing Diffusion Model (rdm) on the shared race core (#353) #502, Add the psychometric function model (psychometric()) #507, fix(conditional_effects): ask brms only for a parameter's own effects on the multinomial route #515, merge dev-1.5.0 into dev-1.6.0 #516, andorigin/dev-1.5.0..origin/434-circmix-equivalence.dev-1.5.0..434-circmix-equivalencewith 180 blobs; mpt(): design-fixed covariates and impossible categories (MPT 3/6, #255) #486 has 138 and Add multinomial processing tree models with mpt() (MPT 1/6, #255) #484 125. A PR range takes 0.6 to 0.9 s cold in a fresh clone and 0.2 s warm. Looking blobs up one by one in ablob:noneclone takes 0.4 s each and aborts after about 50 (48 blobs in 20.6 s, then a fatal error), so the step lists them with--missing=printand fetches them in one request. Afilter: blob:limit=1mcheckout would also work (clone 3.4 s, 52 MB.git, same set of oversize blobs); it is not part of this PR..rds, non-literal paths, prose and comment mentions,eval = FALSE, blockquote and nested fences, subfolders,.qmd, Latin-1), all as expected, plus the realvignettes(26 references, 0 problems). 17 more scratch folders cover the reader rule (readers, writers,.RDScase forreadRDS(),bmm()andbrm(), a reader nested in a writer, positional and computedfits/paths, a missingvignettes/), all as expected. Thevignettes/of nine branches give the same counts before and after the rule:develop,255-mpt-article,434-circmix-equivalence,dev-1.5.0and this branch 26,mv-articleandrace-lnr30,136-vwm-change-detection7, all with 0 problems;308-hypothesis-testing-article9 with 3 problems.editedtrigger, the fits the step could not see and the references it rejected for no reason. The second review's findings are fixed in the commits listed under Changes after review.After merging
Merge this PR first. A release event runs the workflow file at the tagged commit, so the guard has to be on
developbefore the release exists. Then create the release, then markfile-sizerequired ondevelopanddev-*(repository settings, branch protection or rulesets, required status checks; the check is offered once it has run on a PR).A normal release would become "Latest", and
install_github("popov-lab/bmm@*release")would resolve to it.Optional follow-up for #489
The MPT article fits three models on every site build (
fit-2htm,fit-ol,fit-ol-restricted; none usesfile =). Uploading those fits to the release and addingfile = "fits/..."would end that. It needs the release first, so it is not part of this PR.