[PR #184] [MERGED] [application] Declare ports and build the app in a composition root #183

Closed
opened 2026-10-13 19:48:30 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Glitchy-Sheep/boosty-downloader/pull/184
Author: @Glitchy-Sheep
Created: 9/13/2026
Status: ✅ Merged
Merged: 9/13/2026
Merged by: @Glitchy-Sheep

Base: main ← Head: refactor/composition-root


📝 Commits (2)

  • d0db42b [application] Declare ports for the reporter, the post cache and the failure log
  • e884583 [cli] Build the app in a composition root and drop application/di

📊 Changes

26 files changed (+413 additions, -382 deletions)

View changed files

➖ boosty_downloader/application/di/__init__.py (+0 -0)
➖ boosty_downloader/application/di/app_environment.py (+0 -149)
➖ boosty_downloader/application/di/download_context.py (+0 -35)
➖ boosty_downloader/application/di/initialized_app.py (+0 -102)
➕ boosty_downloader/application/download_context.py (+40 -0)
📝 boosty_downloader/application/download_plan.py (+2 -4)
➕ boosty_downloader/application/ports.py (+73 -0)
📝 boosty_downloader/application/post_retry.py (+1 -1)
📝 boosty_downloader/application/use_cases/download_all_posts.py (+1 -1)
📝 boosty_downloader/application/use_cases/download_single_post.py (+1 -1)
📝 boosty_downloader/application/use_cases/download_specific_post.py (+1 -1)
📝 boosty_downloader/application/use_cases/plan_download.py (+2 -4)
📝 boosty_downloader/cli/commands/check.py (+15 -9)
📝 boosty_downloader/cli/commands/download.py (+34 -30)
➕ boosty_downloader/cli/composition_root.py (+158 -0)
📝 boosty_downloader/cli/console_progress_reporter.py (+6 -5)
➕ boosty_downloader/cli/update_check.py (+43 -0)
📝 test/e2e/credentials_scope_test.py (+13 -10)
📝 test/e2e/download_flow_test.py (+5 -3)
📝 test/unit/file_downloader/extension_guessing_test.py (+1 -1)

...and 6 more files

📄 Description

📝 Summary

Audit stage 3, part 1 of 3: the application layer stops importing cli, and the app is built in one place.

🔧 What changes

  • application/ports.py declares three Protocols with the methods the use cases actually call: ProgressReporter (8 methods), PostCache (4) and FailureLog (1). Implementations in cli and infrastructure match structurally and do not import the ports.
  • DownloadContext moves out of application/di into application/download_context.py and is typed with the ports instead of the rich reporter, SQLitePostCache and FailedDownloadsLogger.
  • cli/composition_root.py is the new composition root: load_settings reads config.yaml and applies the CLI overrides, open_app opens the API session (with credentials), the media session (without), the reporter and the cache, and hands them out as App.
  • application/di/ (AppConfig -> AppEnvironment -> Environment) is deleted. The download and check commands read the same way: settings -> update check -> open_app -> use case -> view.
  • The update check lives in cli/update_check.py and is called by the commands.
  • The rich reporter class is now ConsoleProgressReporter, so the port and its implementation do not share a name.

Behaviour

No user-facing change: same start order, same two sessions, same cache location rules (a CLI directory wins over the config value, the cache lands next to the posts unless --cache-dir is set).

🧪 Tests

  • Test fakes for the reporter and the failure log pass without cast; pyright checks them against the ports (two fakes were missing info and got it).
  • credentials_scope_test builds the app through open_app, so it checks the real wiring: token and cookie go to the API only.
  • task ci green locally: ruff, pyright strict, 238 tests + e2e.
  • Live runs into a temporary directory on a small blog: check, download --dry-run -f post_content and the real download -f post_content agree on the media count and size; the cache file lands under --cache-dir.

⏭️ Next

PR 2: cache reset on a corrupted file and clean-cache through a use case. PR 3: DownloadContentTypeFilter into domain and an import-linter layers contract in CI.

✅ Checklist

  • Tested live on a real blog (feature or fix)
  • Updated CLI --help and README if needed
  • Updated docs/ if the download flow or dev process changed
  • Added a line to ## Unreleased in CHANGELOG.md (or the ci/skip-changelog label if not user-facing)

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Glitchy-Sheep/boosty-downloader/pull/184 **Author:** [@Glitchy-Sheep](https://github.com/Glitchy-Sheep) **Created:** 9/13/2026 **Status:** ✅ Merged **Merged:** 9/13/2026 **Merged by:** [@Glitchy-Sheep](https://github.com/Glitchy-Sheep) **Base:** `main` ← **Head:** `refactor/composition-root` --- ### 📝 Commits (2) - [`d0db42b`](https://github.com/Glitchy-Sheep/boosty-downloader/commit/d0db42b2d2b44027c962fdd304a02523c74e7cf8) [application] Declare ports for the reporter, the post cache and the failure log - [`e884583`](https://github.com/Glitchy-Sheep/boosty-downloader/commit/e88458363d508fc0b2ead61d4a283251b92adeb0) [cli] Build the app in a composition root and drop application/di ### 📊 Changes **26 files changed** (+413 additions, -382 deletions) <details> <summary>View changed files</summary> ➖ `boosty_downloader/application/di/__init__.py` (+0 -0) ➖ `boosty_downloader/application/di/app_environment.py` (+0 -149) ➖ `boosty_downloader/application/di/download_context.py` (+0 -35) ➖ `boosty_downloader/application/di/initialized_app.py` (+0 -102) ➕ `boosty_downloader/application/download_context.py` (+40 -0) 📝 `boosty_downloader/application/download_plan.py` (+2 -4) ➕ `boosty_downloader/application/ports.py` (+73 -0) 📝 `boosty_downloader/application/post_retry.py` (+1 -1) 📝 `boosty_downloader/application/use_cases/download_all_posts.py` (+1 -1) 📝 `boosty_downloader/application/use_cases/download_single_post.py` (+1 -1) 📝 `boosty_downloader/application/use_cases/download_specific_post.py` (+1 -1) 📝 `boosty_downloader/application/use_cases/plan_download.py` (+2 -4) 📝 `boosty_downloader/cli/commands/check.py` (+15 -9) 📝 `boosty_downloader/cli/commands/download.py` (+34 -30) ➕ `boosty_downloader/cli/composition_root.py` (+158 -0) 📝 `boosty_downloader/cli/console_progress_reporter.py` (+6 -5) ➕ `boosty_downloader/cli/update_check.py` (+43 -0) 📝 `test/e2e/credentials_scope_test.py` (+13 -10) 📝 `test/e2e/download_flow_test.py` (+5 -3) 📝 `test/unit/file_downloader/extension_guessing_test.py` (+1 -1) _...and 6 more files_ </details> ### 📄 Description ## 📝 Summary Audit stage 3, part 1 of 3: the application layer stops importing cli, and the app is built in one place. ## 🔧 What changes - `application/ports.py` declares three Protocols with the methods the use cases actually call: `ProgressReporter` (8 methods), `PostCache` (4) and `FailureLog` (1). Implementations in cli and infrastructure match structurally and do not import the ports. - `DownloadContext` moves out of `application/di` into `application/download_context.py` and is typed with the ports instead of the rich reporter, `SQLitePostCache` and `FailedDownloadsLogger`. - `cli/composition_root.py` is the new composition root: `load_settings` reads config.yaml and applies the CLI overrides, `open_app` opens the API session (with credentials), the media session (without), the reporter and the cache, and hands them out as `App`. - `application/di/` (`AppConfig` -> `AppEnvironment` -> `Environment`) is deleted. The `download` and `check` commands read the same way: settings -> update check -> `open_app` -> use case -> view. - The update check lives in `cli/update_check.py` and is called by the commands. - The rich reporter class is now `ConsoleProgressReporter`, so the port and its implementation do not share a name. ## Behaviour No user-facing change: same start order, same two sessions, same cache location rules (a CLI directory wins over the config value, the cache lands next to the posts unless `--cache-dir` is set). ## 🧪 Tests - Test fakes for the reporter and the failure log pass without `cast`; pyright checks them against the ports (two fakes were missing `info` and got it). - `credentials_scope_test` builds the app through `open_app`, so it checks the real wiring: token and cookie go to the API only. - `task ci` green locally: ruff, pyright strict, 238 tests + e2e. - Live runs into a temporary directory on a small blog: `check`, `download --dry-run -f post_content` and the real `download -f post_content` agree on the media count and size; the cache file lands under `--cache-dir`. ## ⏭️ Next PR 2: cache reset on a corrupted file and `clean-cache` through a use case. PR 3: `DownloadContentTypeFilter` into domain and an import-linter layers contract in CI. ## ✅ Checklist - [x] Tested live on a real blog (feature or fix) - [x] Updated CLI `--help` and README if needed - [x] Updated `docs/` if the download flow or dev process changed - [x] Added a line to `## Unreleased` in CHANGELOG.md (or the `ci/skip-changelog` label if not user-facing) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Glitchy-Sheep/boosty-downloader#183
No description provided.