edgesync 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. edgesync-0.2.0/.github/workflows/ci.yml +76 -0
  2. edgesync-0.2.0/.github/workflows/release.yml +144 -0
  3. edgesync-0.2.0/.gitignore +221 -0
  4. edgesync-0.2.0/CHANGELOG.md +52 -0
  5. edgesync-0.2.0/CONTRIBUTING.md +87 -0
  6. edgesync-0.2.0/LICENSE +21 -0
  7. edgesync-0.2.0/PKG-INFO +155 -0
  8. edgesync-0.2.0/README.md +127 -0
  9. edgesync-0.2.0/SECURITY.md +32 -0
  10. edgesync-0.2.0/VERSION +1 -0
  11. edgesync-0.2.0/docs/getting-started.md +154 -0
  12. edgesync-0.2.0/docs/integrations.md +170 -0
  13. edgesync-0.2.0/docs/reliability.md +91 -0
  14. edgesync-0.2.0/docs/storage.md +88 -0
  15. edgesync-0.2.0/edgesync/__init__.py +61 -0
  16. edgesync-0.2.0/edgesync/client.py +227 -0
  17. edgesync-0.2.0/edgesync/config.py +70 -0
  18. edgesync-0.2.0/edgesync/exceptions.py +51 -0
  19. edgesync-0.2.0/edgesync/logging.py +16 -0
  20. edgesync-0.2.0/edgesync/models/__init__.py +16 -0
  21. edgesync-0.2.0/edgesync/models/delivery.py +69 -0
  22. edgesync-0.2.0/edgesync/models/message.py +62 -0
  23. edgesync-0.2.0/edgesync/models/receipt.py +21 -0
  24. edgesync-0.2.0/edgesync/models/stats.py +27 -0
  25. edgesync-0.2.0/edgesync/py.typed +0 -0
  26. edgesync-0.2.0/edgesync/queue/__init__.py +5 -0
  27. edgesync-0.2.0/edgesync/queue/manager.py +92 -0
  28. edgesync-0.2.0/edgesync/retry/__init__.py +5 -0
  29. edgesync-0.2.0/edgesync/retry/backoff.py +42 -0
  30. edgesync-0.2.0/edgesync/retry/policy.py +56 -0
  31. edgesync-0.2.0/edgesync/storage/__init__.py +6 -0
  32. edgesync-0.2.0/edgesync/storage/base.py +109 -0
  33. edgesync-0.2.0/edgesync/storage/migrations.py +70 -0
  34. edgesync-0.2.0/edgesync/storage/sqlite.py +529 -0
  35. edgesync-0.2.0/edgesync/transports/__init__.py +7 -0
  36. edgesync-0.2.0/edgesync/transports/base.py +30 -0
  37. edgesync-0.2.0/edgesync/transports/http.py +112 -0
  38. edgesync-0.2.0/edgesync/transports/registry.py +52 -0
  39. edgesync-0.2.0/edgesync/utils/__init__.py +1 -0
  40. edgesync-0.2.0/edgesync/utils/clock.py +50 -0
  41. edgesync-0.2.0/edgesync/utils/ids.py +20 -0
  42. edgesync-0.2.0/edgesync/worker/__init__.py +5 -0
  43. edgesync-0.2.0/edgesync/worker/lifecycle.py +62 -0
  44. edgesync-0.2.0/edgesync/worker/scheduler.py +20 -0
  45. edgesync-0.2.0/edgesync/worker/sync_worker.py +162 -0
  46. edgesync-0.2.0/pyproject.toml +88 -0
  47. edgesync-0.2.0/tests/__init__.py +0 -0
  48. edgesync-0.2.0/tests/conftest.py +8 -0
  49. edgesync-0.2.0/tests/fixtures/__init__.py +0 -0
  50. edgesync-0.2.0/tests/fixtures/factories.py +37 -0
  51. edgesync-0.2.0/tests/fixtures/fake_transport.py +61 -0
  52. edgesync-0.2.0/tests/integration/__init__.py +0 -0
  53. edgesync-0.2.0/tests/integration/test_client.py +170 -0
  54. edgesync-0.2.0/tests/integration/test_http_transport.py +160 -0
  55. edgesync-0.2.0/tests/integration/test_sqlite_storage.py +337 -0
  56. edgesync-0.2.0/tests/integration/test_worker.py +229 -0
  57. edgesync-0.2.0/tests/reliability/__init__.py +0 -0
  58. edgesync-0.2.0/tests/reliability/test_reliability.py +238 -0
  59. edgesync-0.2.0/tests/unit/__init__.py +0 -0
  60. edgesync-0.2.0/tests/unit/test_backoff.py +40 -0
  61. edgesync-0.2.0/tests/unit/test_config.py +58 -0
  62. edgesync-0.2.0/tests/unit/test_models.py +59 -0
  63. edgesync-0.2.0/tests/unit/test_queue_manager.py +46 -0
  64. edgesync-0.2.0/tests/unit/test_retry_policy.py +30 -0
  65. edgesync-0.2.0/uv.lock +743 -0
@@ -0,0 +1,76 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: test (py${{ matrix.python-version }})
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v3
22
+ with:
23
+ enable-cache: true
24
+
25
+ - name: Set up Python ${{ matrix.python-version }}
26
+ run: uv python install ${{ matrix.python-version }}
27
+
28
+ - name: Install dependencies
29
+ run: uv sync --python ${{ matrix.python-version }} --all-extras --dev
30
+
31
+ - name: Run tests
32
+ run: uv run --python ${{ matrix.python-version }} pytest --cov=edgesync --cov-report=xml
33
+
34
+ - name: Upload coverage
35
+ if: matrix.python-version == '3.13'
36
+ uses: codecov/codecov-action@v4
37
+ with:
38
+ files: ./coverage.xml
39
+ fail_ci_if_error: false
40
+
41
+ lint:
42
+ name: lint
43
+ runs-on: ubuntu-latest
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+
47
+ - name: Install uv
48
+ uses: astral-sh/setup-uv@v3
49
+ with:
50
+ enable-cache: true
51
+
52
+ - name: Install dependencies
53
+ run: uv sync --all-extras --dev
54
+
55
+ - name: Ruff check
56
+ run: uv run ruff check .
57
+
58
+ - name: Ruff format check
59
+ run: uv run ruff format --check .
60
+
61
+ typecheck:
62
+ name: typecheck
63
+ runs-on: ubuntu-latest
64
+ steps:
65
+ - uses: actions/checkout@v4
66
+
67
+ - name: Install uv
68
+ uses: astral-sh/setup-uv@v3
69
+ with:
70
+ enable-cache: true
71
+
72
+ - name: Install dependencies
73
+ run: uv sync --all-extras --dev
74
+
75
+ - name: Mypy
76
+ run: uv run mypy
@@ -0,0 +1,144 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ jobs:
9
+ test:
10
+ name: test (py${{ matrix.python-version }})
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v3
21
+ with:
22
+ enable-cache: true
23
+
24
+ - name: Set up Python ${{ matrix.python-version }}
25
+ run: uv python install ${{ matrix.python-version }}
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --python ${{ matrix.python-version }} --all-extras --dev
29
+
30
+ - name: Run tests
31
+ run: uv run --python ${{ matrix.python-version }} pytest
32
+
33
+ - name: Ruff check
34
+ if: matrix.python-version == '3.13'
35
+ run: uv run ruff check .
36
+
37
+ - name: Ruff format check
38
+ if: matrix.python-version == '3.13'
39
+ run: uv run ruff format --check .
40
+
41
+ - name: Mypy
42
+ if: matrix.python-version == '3.13'
43
+ run: uv run mypy
44
+
45
+ check-version:
46
+ name: verify tag matches VERSION file
47
+ runs-on: ubuntu-latest
48
+ needs: test
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+
52
+ - name: Compare tag to VERSION file
53
+ run: |
54
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
55
+ FILE_VERSION="$(tr -d '[:space:]' < VERSION)"
56
+ if [ "$TAG_VERSION" != "$FILE_VERSION" ]; then
57
+ echo "::error::tag v$TAG_VERSION does not match VERSION file ($FILE_VERSION)"
58
+ exit 1
59
+ fi
60
+
61
+ build:
62
+ name: build distributions
63
+ runs-on: ubuntu-latest
64
+ needs: check-version
65
+ steps:
66
+ - uses: actions/checkout@v4
67
+
68
+ - name: Install uv
69
+ uses: astral-sh/setup-uv@v3
70
+ with:
71
+ enable-cache: true
72
+
73
+ - name: Build sdist and wheel
74
+ run: uv build
75
+
76
+ - name: Upload distributions
77
+ uses: actions/upload-artifact@v4
78
+ with:
79
+ name: dist
80
+ path: dist/
81
+
82
+ publish-pypi:
83
+ name: publish to PyPI
84
+ runs-on: ubuntu-latest
85
+ needs: build
86
+ environment:
87
+ name: pypi
88
+ url: https://pypi.org/project/edgesync/
89
+ permissions:
90
+ id-token: write # required for PyPI Trusted Publishing (OIDC)
91
+ steps:
92
+ - name: Download distributions
93
+ uses: actions/download-artifact@v4
94
+ with:
95
+ name: dist
96
+ path: dist/
97
+
98
+ - name: Publish to PyPI
99
+ uses: pypa/gh-action-pypi-publish@release/v1
100
+ with:
101
+ # VERSION is the single source of truth for the release version;
102
+ # if this version is already published (e.g. the workflow was
103
+ # re-run, or a tag was pushed for a version already on PyPI),
104
+ # skip the upload instead of failing the release.
105
+ skip-existing: true
106
+
107
+ github-release:
108
+ name: create GitHub release
109
+ runs-on: ubuntu-latest
110
+ needs: publish-pypi
111
+ permissions:
112
+ contents: write # required to create the GitHub release
113
+ steps:
114
+ - uses: actions/checkout@v4
115
+
116
+ - name: Download distributions
117
+ uses: actions/download-artifact@v4
118
+ with:
119
+ name: dist
120
+ path: dist/
121
+
122
+ - name: Extract CHANGELOG section for this version
123
+ id: changelog
124
+ run: |
125
+ VERSION="$(tr -d '[:space:]' < VERSION)"
126
+ NOTES_FILE="$(mktemp)"
127
+ awk -v ver="$VERSION" '
128
+ $0 ~ "^## \\[" ver "\\]" { found=1; next }
129
+ found && (/^## \[/ || /^\[[^]]+\]: /) { exit }
130
+ found { print }
131
+ ' CHANGELOG.md > "$NOTES_FILE"
132
+ if [ ! -s "$NOTES_FILE" ]; then
133
+ echo "::error::no CHANGELOG.md section found for [$VERSION]"
134
+ exit 1
135
+ fi
136
+ echo "notes_file=$NOTES_FILE" >> "$GITHUB_OUTPUT"
137
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
138
+
139
+ - name: Create GitHub release
140
+ uses: softprops/action-gh-release@v2
141
+ with:
142
+ name: EdgeSync v${{ steps.changelog.outputs.version }}
143
+ body_path: ${{ steps.changelog.outputs.notes_file }}
144
+ files: dist/*
@@ -0,0 +1,221 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+ # Temporary file for partial code execution
204
+ tempCodeRunnerFile.py
205
+
206
+ # Ruff stuff:
207
+ .ruff_cache/
208
+
209
+ # PyPI configuration file
210
+ .pypirc
211
+
212
+ # Marimo
213
+ marimo/_static/
214
+ marimo/_lsp/
215
+ __marimo__/
216
+
217
+ # Streamlit
218
+ .streamlit/secrets.toml
219
+ CLAUDE.md
220
+ .serena
221
+ .claude
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
6
+ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.2.0] - 2026-08-23
11
+
12
+ ### Added
13
+
14
+ - [docs/integrations.md](docs/integrations.md): usage guidance for plain `asyncio`,
15
+ FastAPI, and Flask, including a ready-to-use thread bridge for running EdgeSync from
16
+ Flask's synchronous request handlers.
17
+
18
+ ### Changed
19
+
20
+ - The package version is now maintained in a single `VERSION` file at the repository root
21
+ (read by `pyproject.toml` via Hatchling and by `edgesync.__version__` at runtime) instead
22
+ of being duplicated across files.
23
+ - The release workflow now verifies the pushed tag matches `VERSION` before building, and
24
+ skips the PyPI upload (instead of failing) if that version has already been published.
25
+ - GitHub release titles and notes are now generated from the matching `CHANGELOG.md`
26
+ section for the released version, instead of GitHub's generic auto-generated notes.
27
+
28
+ ## [0.1.0] - 2026-08-23
29
+
30
+ ### Added
31
+
32
+ - `EdgeSync` client with `publish()`, `stats()`, `start()`/`close()`, and async context
33
+ manager support.
34
+ - Durable SQLite-backed queue (`SQLiteStorage`) with WAL mode, configurable `synchronous`
35
+ durability, and lease-fenced message ownership.
36
+ - Background `SyncWorker` with configurable polling, batch size, and concurrency.
37
+ - Configurable retry policy (`RetryPolicy`) with exponential backoff, jitter, and optional
38
+ `max_attempts` before dead-lettering.
39
+ - Dead-letter queue (`sync.dead_letters`) for inspecting, retrying, and deleting messages
40
+ that permanently failed or exhausted retries.
41
+ - `HTTPTransport` with configurable idempotency header, success/retryable status code
42
+ classification, and connection reuse.
43
+ - Pluggable `Transport` interface for non-HTTP destinations.
44
+ - Multiple named destinations via `destinations={...}` / `destination=` routing.
45
+ - Configurable queue capacity (`max_messages`, `max_storage_bytes`) with
46
+ `REJECT_NEW` / `DROP_OLDEST` / `DROP_LOWEST_PRIORITY` / `DEAD_LETTER` overflow policies.
47
+ - Crash and restart recovery via expired-lease sweeps, safe across concurrent workers and
48
+ processes sharing the same database file.
49
+
50
+ [Unreleased]: https://github.com/adhuldas/EdgeSync/compare/v0.2.0...HEAD
51
+ [0.2.0]: https://github.com/adhuldas/EdgeSync/compare/v0.1.0...v0.2.0
52
+ [0.1.0]: https://github.com/adhuldas/EdgeSync/releases/tag/v0.1.0
@@ -0,0 +1,87 @@
1
+ # Contributing to EdgeSync
2
+
3
+ Thanks for considering a contribution. This project is a small, dependency-light library, and
4
+ that's intentional — please keep that in mind when proposing changes.
5
+
6
+ ## Setup
7
+
8
+ This project uses [uv](https://docs.astral.sh/uv/) for dependency management.
9
+
10
+ ```bash
11
+ git clone https://github.com/adhuldas/EdgeSync.git
12
+ cd EdgeSync
13
+ uv sync
14
+ ```
15
+
16
+ ## Development workflow
17
+
18
+ Run the full check suite before opening a PR:
19
+
20
+ ```bash
21
+ uv run pytest # tests
22
+ uv run ruff check . # lint
23
+ uv run ruff format --check . # formatting
24
+ uv run mypy # type check (strict, on edgesync/)
25
+ ```
26
+
27
+ All four must pass. CI runs the same checks on every PR across the supported Python versions
28
+ (3.10–3.13).
29
+
30
+ ## Code style
31
+
32
+ - Type hints are required everywhere in `edgesync/`; `mypy --strict` is enforced.
33
+ - Business logic must go through the `Clock` abstraction (`edgesync.utils.clock`) instead of
34
+ calling `datetime.now()` directly, so tests can use `FixedClock` instead of sleeping or
35
+ racing wall-clock time.
36
+ - Transports only attempt delivery and classify the outcome (`DeliveryResult`) — they never
37
+ touch storage or retry scheduling. That's the worker's job.
38
+ - Keep the public surface (`edgesync/__init__.py`) intentional: only export what's meant to
39
+ be part of the stable API.
40
+
41
+ ## Tests
42
+
43
+ Tests are organized by what they exercise:
44
+
45
+ - `tests/unit/` — pure logic with no I/O (retry backoff, config validation, models).
46
+ - `tests/integration/` — a single component against a real dependency (SQLite, HTTP via
47
+ `respx`, the worker against a fake transport, the client end-to-end).
48
+ - `tests/reliability/` — crash, restart, and concurrency scenarios that exercise the
49
+ interaction between storage and the worker (e.g. lease expiry recovery, concurrent workers
50
+ never double-claiming).
51
+
52
+ New behavior should come with a test in the appropriate tier. Reliability-sensitive changes
53
+ (anything touching `storage/`, `worker/`, or lease handling) should include or update a
54
+ `tests/reliability/` case.
55
+
56
+ ## Commit / PR expectations
57
+
58
+ - Keep PRs focused — one logical change per PR.
59
+ - Write commit messages and PR descriptions that explain *why*, not just *what*.
60
+ - If you're changing behavior described in `docs/`, update the relevant doc in the same PR.
61
+
62
+ ## Releasing
63
+
64
+ `VERSION` (repo root) is the single source of truth for the package version — it's read by
65
+ `pyproject.toml` (via Hatchling) and by `edgesync.__version__` at runtime. To cut a release:
66
+
67
+ 1. Bump the version in `VERSION` (no `v` prefix, e.g. `0.2.0`).
68
+ 2. Move the `[Unreleased]` entries in `CHANGELOG.md` under a new `## [0.2.0] - YYYY-MM-DD`
69
+ heading.
70
+ 3. Commit those two changes.
71
+ 4. Tag the commit and push the tag: `git tag v0.2.0 && git push origin v0.2.0`.
72
+
73
+ Pushing a `vX.Y.Z` tag triggers `.github/workflows/release.yml`, which runs the full test
74
+ suite, verifies the tag matches `VERSION`, builds the sdist/wheel, and publishes to PyPI via
75
+ Trusted Publishing. If that version is already on PyPI (e.g. the workflow is re-run), the
76
+ publish step is skipped rather than failing.
77
+
78
+ ## Reporting bugs
79
+
80
+ Open a GitHub issue with:
81
+ - EdgeSync version and Python version
82
+ - A minimal reproduction (ideally a failing test)
83
+ - Expected vs. actual behavior
84
+
85
+ ## Reporting security issues
86
+
87
+ Do not open a public issue for security vulnerabilities — see [SECURITY.md](SECURITY.md).
edgesync-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EdgeSync Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.