diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index de0203c888..feb74e2f0b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,7 +41,17 @@ jobs: run: boxlang --version - name: Install Dev Dependencies - run: box install id=testbox --verbose --nosave + run: | + # Pinned to the bleeding-edge channel rather than the default + # latest-stable release - testbox@be picks up + # Ortus-Solutions/TestBox#201's fix for a real TestBox bug + # (server.cli.parsed.positionals leaking the runner's own + # invocation path as argv[0], misread as a bundle argument - + # ClassNotFoundBoxLangException on BoxLangRunner.bx) before that + # fix has made it into a tagged stable release. Revert to the + # unpinned `id=testbox` once a stable TestBox release carrying + # the fix ships. + box install id=testbox@be --verbose --nosave - name: Test Module run: | diff --git a/changelog.md b/changelog.md index 7a6af0644a..34880f2983 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +* Pinned `tests.yml`'s `box install id=testbox` (this repo's own CI test-suite dependency) to `testbox@be` (bleeding-edge) instead of the unpinned default. CI was intermittently failing before a single test spec ran, with `ClassNotFoundBoxLangException` on TestBox's own `BoxLangRunner.bx` - traced to a real TestBox bug ([Ortus-Solutions/TestBox#201](https://github.com/Ortus-Solutions/TestBox/pull/201)): `server.cli.parsed.positionals` on current BoxLang engine builds leaks the runner's own invocation path as its first entry (an argv[0] leak), which `BoxLangRunner.bx` misread as a user-supplied bundle argument whenever no real one was given. `testbox@be` picks up that fix ahead of a tagged stable release - revert to the unpinned `id=testbox` once one ships it. +* Fixed `FunctionsLoader.bx` and `ThemeRenderer.bx` intermittently failing a real `bxSites build` with `functions.bxs at [...] failed to load: The template path [...] could not be found` (or the same for a theme's `layout.bxm`), even though the file genuinely exists - observed on this repository's own "Publish Docs" CI, where roughly a third of ten otherwise-identical theme-matrix jobs hit this on `functionsLoader.load()` (the very first `include` the whole build performs), immediately after `fileExists()` on that same path had just confirmed it was there. Root cause: a bare `include` statement doesn't resolve an OS absolute filesystem path at all - BoxLang coerces an absolute-looking path against registered mappings/webroot instead, so `include functionsPath`/`include arguments.themeDir & "/layout.bxm"` were never a reliably supported way to reach an arbitrary, runtime-computed directory, not a timing race. Both now register a runtime mapping (`getBoxRuntime().getConfiguration().registerMapping()`) for the target directory and include through that mapping-prefixed path instead - the actually-supported mechanism. * Fixed `.github/workflows/pages.yml` (this repo's own GitHub Pages deploy) never actually running since the `versions.default` cutover - its trigger/deploy condition was changed to `main`, a branch that doesn't exist yet in this repository (`development` is still this project's only real branch, actively working toward the 1.0.0 release; `main` will only come into being once `development` is later merged into it to cut that release, at which point `main` becomes the frozen `1.0.x` line). Moved the trigger and the `Deploy to the site root` step's condition back to `development` so the versioned site (1.0.x at the root, `/next/` for work in progress) actually publishes again. * **A page's own frontmatter is now available to `{{ }}` variable expressions, no `bxsites.yaml` entry needed.** Previously `{{ dotted.path }}` only ever resolved against `bxsites.yaml`'s site-wide `variables` block; a page's own frontmatter values (title, summary, or any custom key a project defines) were reachable from a magic function's bare `page` reference but not from plain `{{ }}` markdown. `DocsLoader.bx`/`BlogDiscoverer.bx` now also preserve the raw, unfiltered frontmatter struct on every loaded page/post as `page.frontmatter` (previously only a fixed, named set of fields survived - a custom key like `product: BoxLang` was silently dropped); `BuildPipeline.bx`'s `convertMarkdown()` merges that page's own struct into `VariablesProcessor.bx`'s lookup scope under a reserved `page` key, so `{{ page.title }}` and `{{ page.frontmatter.product }}` resolve through the exact same dotted-path mechanism `{{ company }}` already does, with zero changes needed inside `VariablesProcessor.bx` itself. `page` joins the existing reserved "supporting variable" names (already reserved for a magic function's own bare reference) - a `variables.page` entry, if a project somehow declared one, is shadowed by the current page's own struct. See `docs/guides/variables-and-functions.md#page-variables` diff --git a/models/build/FunctionsLoader.bx b/models/build/FunctionsLoader.bx index 811ef6f1fc..f6fd80165f 100644 --- a/models/build/FunctionsLoader.bx +++ b/models/build/FunctionsLoader.bx @@ -94,9 +94,27 @@ class { return {} } + // `include` doesn't resolve a bare OS absolute path against the real + // filesystem - BoxLang coerces an absolute-looking path against + // registered mappings/webroot instead, so `include functionsPath` + // here intermittently threw "The template path [...] could not be + // found" for this exact path, moments after `fileExists()` just + // above had already confirmed it was there - not a race, just an + // unsupported way to reach an arbitrary, runtime-computed + // directory. Registering a mapping for `dir` and including through + // that instead is the supported path (see + // `getBoxRuntime().getConfiguration().registerMapping()` - BoxLang + // docs' "Includes"/"Mappings & Class Resolution" pages). A fixed, + // module-namespaced prefix is safe to re-register on every call + // (cheap, and this class's own singleton is reused with a + // potentially different `docsDir` across `bxSites serve` rebuilds) - + // it simply repoints the same prefix at the current `dir`. + var mappingPrefix = "/bxsitesFunctionsInclude" + getBoxRuntime().getConfiguration().registerMapping( mappingPrefix, dir ) + var before = structKeyArray( variables ) try { - include functionsPath + include "#mappingPrefix#/functions.bxs" } catch ( any e ) { throw( type : "BxSites.InvalidFunctions", diff --git a/models/build/ThemeRenderer.bx b/models/build/ThemeRenderer.bx index 95a454e5ab..fad9288d0e 100644 --- a/models/build/ThemeRenderer.bx +++ b/models/build/ThemeRenderer.bx @@ -259,9 +259,23 @@ class { variables[ fnName ] = arguments.functions[ fnName ] } + // Same fix as FunctionsLoader.bx's own `load()` - a bare `include` + // statement doesn't resolve an OS absolute path against the real + // filesystem (BoxLang coerces it against registered mappings/webroot + // instead), so `include arguments.themeDir & "/layout.bxm"` + // intermittently threw "The template path [...] could not be found" + // for a path this class's own caller already resolved to a real, + // existing directory. Registering a mapping and including through + // that instead is the supported path. Cheap to re-register on every + // call (this method runs once per page) - a fixed, module-namespaced + // prefix simply gets repointed at the current page's own themeDir, + // which never actually changes mid-build (one theme per build). + var themeMappingPrefix = "/bxsitesThemeInclude" + getBoxRuntime().getConfiguration().registerMapping( themeMappingPrefix, arguments.themeDir ) + var html = "" bx:savecontent variable="html" { - include arguments.themeDir & "/layout.bxm" + include "#themeMappingPrefix#/layout.bxm" } return html }