Skip to content

fix(self-packaging #62): bundled-mode HTTP serving end-to-end#69

Merged
jrosskopf merged 1 commit into
mainfrom
feature/gh-62-bundled-mode-http
May 25, 2026
Merged

fix(self-packaging #62): bundled-mode HTTP serving end-to-end#69
jrosskopf merged 1 commit into
mainfrom
feature/gh-62-bundled-mode-http

Conversation

@jrosskopf
Copy link
Copy Markdown
Contributor

Summary

Surfaced while planning the integration test for #62: bundled-mode
HTTP serving was broken on main
, not just untested. Packing
examples/ and running the result from an empty cwd crashed with:

[INFO] Bundle detected and registered (386 entries)
[INFO] Loading configuration file: "flapi.yaml"
[ERROR] Could not open file: flapi.yaml
Aborted (core dumped)

The EmbeddedArchiveFileProvider was registered globally by
detectAndRegisterEmbeddedBundle, but the config / endpoint /
template loaders bypassed it via std::ifstream and
std::filesystem::recursive_directory_iterator. All existing
self-packaging tests are CLI-level (pack, info, unpack,
idempotence, truncation fallback); nobody had ever started the
bundled binary as a server, which is exactly why #62 was deferred
in PR #56.

This PR fixes the underlying machinery and adds the three pytest
cases that were originally scoped for #62.

Changes

Provider-aware reads (src/extended_yaml_parser.cpp)

  • parseFile and the static loadYamlFile now read via
    FileProviderFactory::CreateProvider(path)->ReadFile(...).
  • resolveIncludePath swaps std::filesystem::exists for a
    provider-aware ConfigFileExists so {{include from ...}}
    directives find their targets inside the bundle.

Provider-aware path resolution (src/config_manager.cpp)

  • parseTemplateConfig: in bundled mode, skip the
    std::filesystem::absolute() step -- the template path is a
    bundle key like sqls, not a filesystem path. Turning it into
    /cwd/sqls prevents the embedded provider from finding it.
  • loadEndpointConfigsRecursively: in bundled mode, enumerate
    .yaml/.yml entries by prefix-scanning the bundle map.
    EmbeddedArchiveFileProvider::ListFiles is non-recursive by
    contract, so the walk is inlined.
  • getFileProvider: in bundled mode, return a fresh provider from
    FileProviderFactory so SQLTemplateProcessor reads SQL
    templates from the bundle rather than the LocalFileProvider
    baked into ConfigLoader.

Endpoint-vs-template path collision
(src/sql_template_processor.cpp)

  • getFullTemplatePath: when the endpoint parser has already
    resolved templateSource against its YAML's parent dir (which
    in bundled mode produces a full bundle key like sqls/hello.sql),
    don't re-prepend the basePath. Mirrors the existing absolute-path
    short-circuit just above; this prevented the previous
    sqls/sqls/hello.sql lookup miss.

Test (test/integration/test_self_packaging_http.py)
Three new pytest cases covering the deferred behaviours:

Test plan

  • cmake --build build/release clean
  • ctest -- 637 / 637 pass serially
  • pytest test/integration/test_self_packaging.py test_self_packaging_http.py test_env_overrides.py -v
    -- all 17 pass (8 existing CLI-level + 3 new HTTP + 6 env overrides)
  • Manual smoke: packed examples/, started from clean cwd, hit
    /hello -> 200; hit /cities reading embed://data/cities.csv
    -> 200 with 3 expected rows

Notes

  • Uses --port instead of FLAPI_PORT in the new tests so this PR
    doesn't depend on feat: 12-factor env vars FLAPI_PORT + FLAPI_HOST (#63) #67 (FLAPI_PORT / FLAPI_HOST).
  • clang-tidy flagged unrelated pre-existing warnings in the touched
    files (std::endl, push_back vs emplace_back, etc.); left
    untouched per the no-incidental-cleanup convention.

Closes #62.

Before this change, `flapi pack` produced a working binary at the CLI
level (`info`, `unpack`, etc.) but `./flapi-bundled` started from a
clean cwd crashed with "Could not open file: flapi.yaml" -- the
EmbeddedArchiveFileProvider was registered globally but the config /
endpoint / template loaders bypassed it via std::ifstream and
std::filesystem::recursive_directory_iterator.

Four call sites had to be routed through FileProviderFactory so the
in-memory archive is actually consulted when SetBundleContents has
fired:

- extended_yaml_parser: parseFile and the static loadYamlFile now
  read via FileProviderFactory::CreateProvider(path)->ReadFile(...)
  instead of std::ifstream. resolveIncludePath swaps
  std::filesystem::exists for a provider-aware ConfigFileExists so
  {{include from ...}} directives find their targets inside the
  bundle.
- config_manager::parseTemplateConfig: in bundled mode, skip the
  std::filesystem::absolute() step -- the template path is a bundle
  key like "sqls", not a filesystem path; turning it into
  "/cwd/sqls" prevents the embedded provider from finding it.
- config_manager::loadEndpointConfigsRecursively: in bundled mode,
  enumerate .yaml/.yml entries by prefix-scanning the bundle map
  instead of std::filesystem::recursive_directory_iterator. The
  EmbeddedArchiveFileProvider's ListFiles is non-recursive by
  contract, so the walk is inlined here.
- config_manager::getFileProvider: in bundled mode, return a fresh
  provider from FileProviderFactory so SQLTemplateProcessor reads
  SQL templates from the bundle rather than the LocalFileProvider
  baked into ConfigLoader.

Plus a small fix in sql_template_processor::getFullTemplatePath:
when the endpoint parser has already resolved templateSource against
its YAML's parent dir (which in bundled mode produces a full bundle
key like "sqls/hello.sql"), don't re-prepend the basePath. Mirrors
the existing absolute-path short-circuit just above.

test_self_packaging_http.py adds the three pytest cases from the
issue acceptance:
- #1 unbundled flapi serves /hello with 200 + body
- #3 bundled flapi from clean cwd (with the source tree deleted)
  serves the same /hello
- #9 a SQL template using read_csv('embed://data/cities.csv')
  returns the bundled CSV rows over HTTP

All 17 integration tests pass (existing CLI-level self-packaging
suite + env-override suite + the 3 new HTTP cases). 637/637 C++ unit
tests pass.

Closes #62.
@jrosskopf jrosskopf merged commit 1779bc9 into main May 25, 2026
21 checks passed
@jrosskopf jrosskopf deleted the feature/gh-62-bundled-mode-http branch May 25, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bundled-mode HTTP end-to-end integration test (spike behaviours #1, #3, #9)

1 participant