Skip to content

feat: open-source readiness — Apache 2.0, ETL refactor, CI scaffolding#1

Merged
igor-ctrl merged 5 commits into
mainfrom
feat/oss-readiness
Apr 16, 2026
Merged

feat: open-source readiness — Apache 2.0, ETL refactor, CI scaffolding#1
igor-ctrl merged 5 commits into
mainfrom
feat/oss-readiness

Conversation

@igor-ctrl

Copy link
Copy Markdown
Owner

Summary

Prepares the repo for public release. Bundles three threads of work:

  1. License switch from MIT to Apache 2.0 (aligns with dlt, Airflow, Spark ecosystem; explicit patent grant helps enterprise adopters)
  2. ETL refactor — splits the dlt source into a generic layer (usable for any BC tenant) and a bcli-aware bridge
  3. Repo hygiene — GitHub scaffolding, OSS docs, clean references

What changed

License (chore: prepare repo for open-source release)

  • LICENSE: MIT → Apache 2.0 (official text from apache.org)
  • NOTICE added (required by Apache 2.0 §4(d))
  • pyproject.toml: license = \"Apache-2.0\", classifier updated, project.urls added
  • README shields (CI, license, Python versions)
  • Added CONTRIBUTING.md, CODE_OF_CONDUCT.md (Contributor Covenant 2.1), SECURITY.md at root
  • .github/:
    • workflows/tests.yml — pytest + ruff across Python 3.11/3.12/3.13
    • ISSUE_TEMPLATE/bug_report.md + feature_request.md
    • PULL_REQUEST_TEMPLATE.md
  • Scrubbed all bc-cli references from README, CLAUDE.md, docs

ETL refactor (refactor: split bcli.etl into generic + bridge layers)

Before: src/bcli/etl/_source.py mixed generic dlt logic with bcli-specific defaults (registry dependency, always-on multi-company, always-on Fivetran stamping).

After:

```
src/bcli/etl/
├── _generic.py # business_central() factory, EntityDef — NO bcli imports
├── _client.py # minimal httpx + AuthProvider-based BC client
├── _auth.py # AuthProvider protocol + ClientCredentialsAuth + StaticTokenAuth
├── _stampers.py # fivetran_stamper, audit_stamper, company_id_stamper
└── _bridge.py # bcli_profile() — ONLY module importing bcli.*
```

The generic layer works with any BC tenant via explicit credentials + entity list:

```python
from bcli.etl import business_central, EntityDef, fivetran_stamper

source = business_central(
tenant_id="...", client_id="...", client_secret="...",
environment="Production",
entities=[EntityDef(name="customers")],
multi_company=True,
stampers=[fivetran_stamper()],
)
```

The bridge preserves today's Fivetran-parity defaults:

```python
from bcli.etl import bcli_profile
source = bcli_profile(profile="prod") # multi_company=True, fivetran_compat=True
```

Key design constraint: an AST-based test in tests/test_etl/test_generic.py fails the build if any generic-layer module imports from bcli.registry, bcli.client, bcli.config, bcli.auth, or bcli.errors.

Style (style: ruff auto-fixes)

Routine cleanup across src/ and tests/ — unused imports, redundant f-string prefixes, unused locals. No behavior changes.

CLI unchanged

`bcli etl sync` works exactly the same; the cron job on the server keeps producing identical output. The bridge preserves multi-company + Fivetran audit columns by default.

Stats

39 files changed, +1811 / −695

  • 3 new modules (_generic, _client, _auth, _stampers, _bridge replace _source + _entities)
  • 3 new test files (test_generic, test_bridge, test_stampers — 28 tests)
  • 181 total tests passing
  • Lint clean (`ruff check src/ tests/` — zero errors)

Test plan

  • `uv run pytest tests/ -v` — 181 passed
  • `uv run ruff check src/ tests/` — clean
  • `uv run bcli --profile prod etl entities` — 114 entities listed
  • Generic layer has no bcli coupling (AST-enforced test passes)
  • `grep -rn bc-cli src/ docs/ README.md` — no matches
  • `grep -rni beautech src/ tests/ README.md` — no matches
  • Production cron unchanged — still syncs GL entries every 10 min

Follow-ups (out of scope for this PR)

  • Replace the bridge's `bcli.AsyncBCClient` token fetch with a pure-MSAL call in the generic layer (once we're confident the AuthProvider shape is right)
  • OData `$metadata` auto-discovery of entities
  • Publish to PyPI as Apache 2.0

- Switch license from MIT to Apache 2.0 (aligns with dlt, Airflow, Spark ecosystem)
- Add NOTICE file (Apache 2.0 §4(d))
- Add CONTRIBUTING.md, CODE_OF_CONDUCT.md (Contributor Covenant 2.1), SECURITY.md
- Add .github/ scaffolding: tests workflow (Py 3.11/3.12/3.13), issue templates, PR template
- pyproject.toml: project.urls, expanded classifiers, etl keyword
- Scrub bc-cli references in README, CLAUDE.md, docs/contributing.md (repo is 'bcli')
- Add shields to README (CI, license, Python versions)
- Add ETL pipeline section to README
The ETL module now has a clean separation between reusable dlt-source
logic and bcli-specific integration:

- _generic.py: business_central() factory, EntityDef — no bcli imports
- _client.py: minimal httpx + AuthProvider-based BC client
- _auth.py: AuthProvider protocol + ClientCredentialsAuth + StaticTokenAuth
- _stampers.py: fivetran_stamper, audit_stamper, company_id_stamper
- _bridge.py: bcli_profile() — the ONLY module importing bcli.*

The generic layer now works with any BC tenant via explicit credentials +
entity list. The bridge wraps it to reuse bcli profiles and custom-API
registries, with Fivetran-parity defaults (multi-company on, Fivetran
audit columns on).

CLI behavior unchanged: `bcli etl sync` still works the same way.

Public API:
  from bcli.etl import business_central, EntityDef, fivetran_stamper
  from bcli.etl import bcli_profile  # the bridge

Test coverage restructured into test_generic.py (layer isolation including
an AST-based import-rule guard), test_bridge.py, test_stampers.py.

Synthetic tests/fixtures/sample_postman_collection.json replaces a
developer-local Postman file for CI-portable importer tests.
Apply `ruff check --fix` cleanup in prep for CI lint gate:
- Remove unused imports
- Remove redundant f-string prefixes on literals
- Remove unused local variables in tests

No behavior changes.
@cursor

cursor Bot commented Apr 16, 2026

Copy link
Copy Markdown

You have used all of your free Bugbot PR reviews.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

The original workflow had two issues:
1. `uv python install` downloads an interpreter but doesn't activate it —
   uv fell back to the runner's system Python (3.12.3) regardless of matrix.
2. `uv pip install --system` on Ubuntu's externally-managed Python hits
   PEP 668 and refuses to install.

Fix by passing `python-version` to setup-uv (it activates the matrix
version) and installing into a venv via `uv pip install`.
setup-uv@v5 already creates and activates a .venv when python-version is
passed, so the explicit `uv venv` step collides. `uv pip install -e` then
installs directly into the activated venv.
@igor-ctrl igor-ctrl merged commit 188b276 into main Apr 16, 2026
3 checks passed
igor-ctrl added a commit that referenced this pull request Apr 29, 2026
…context

Fixes the #1 pain point when Claude Code uses bcli: -f/--format now works
directly on get/post/patch/delete commands (not just as a global flag).

New commands:
- `bcli endpoint fields <name>` — fetches one record and prints field names
  with inferred types. Makes OData filter construction trivial for LLMs.
- `bcli ai-context` — dumps markdown usage instructions for CLAUDE.md.
  Covers flag syntax, OData filters, common patterns, endpoint discovery.

Bug fixes (found by Codex review):
- `--top 0` and `--skip 0` no longer silently ignored (was truthy check)

Other:
- Help text on `get` command now shows examples with correct syntax
- Auto-quiet when local --format is machine-readable (json/csv/ndjson/raw)
igor-ctrl added a commit that referenced this pull request Apr 29, 2026
feat: open-source readiness — Apache 2.0, ETL refactor, CI scaffolding
@igor-ctrl igor-ctrl deleted the feat/oss-readiness branch April 29, 2026 22:00
igor-ctrl added a commit that referenced this pull request May 4, 2026
…context

Fixes the #1 pain point when Claude Code uses bcli: -f/--format now works
directly on get/post/patch/delete commands (not just as a global flag).

New commands:
- `bcli endpoint fields <name>` — fetches one record and prints field names
  with inferred types. Makes OData filter construction trivial for LLMs.
- `bcli ai-context` — dumps markdown usage instructions for CLAUDE.md.
  Covers flag syntax, OData filters, common patterns, endpoint discovery.

Bug fixes (found by Codex review):
- `--top 0` and `--skip 0` no longer silently ignored (was truthy check)

Other:
- Help text on `get` command now shows examples with correct syntax
- Auto-quiet when local --format is machine-readable (json/csv/ndjson/raw)
igor-ctrl added a commit that referenced this pull request May 4, 2026
feat: open-source readiness — Apache 2.0, ETL refactor, CI scaffolding
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.

1 participant