hlsync 1.20.1__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.

Potentially problematic release.


This version of hlsync might be problematic. Click here for more details.

Files changed (37) hide show
  1. hlsync-1.20.1/LICENSE +106 -0
  2. hlsync-1.20.1/MANIFEST.in +3 -0
  3. hlsync-1.20.1/PKG-INFO +163 -0
  4. hlsync-1.20.1/README.md +129 -0
  5. hlsync-1.20.1/build_backend/hlsync_build.py +32 -0
  6. hlsync-1.20.1/build_backend/versioning.py +75 -0
  7. hlsync-1.20.1/pyproject.toml +71 -0
  8. hlsync-1.20.1/setup.cfg +4 -0
  9. hlsync-1.20.1/src/hlsync/__init__.py +5 -0
  10. hlsync-1.20.1/src/hlsync/__main__.py +4 -0
  11. hlsync-1.20.1/src/hlsync/_version.py +2 -0
  12. hlsync-1.20.1/src/hlsync/cli.py +3149 -0
  13. hlsync-1.20.1/src/hlsync/comparison.py +278 -0
  14. hlsync-1.20.1/src/hlsync/config.py +583 -0
  15. hlsync-1.20.1/src/hlsync/gitignore.py +98 -0
  16. hlsync-1.20.1/src/hlsync/pattern_operands.py +33 -0
  17. hlsync-1.20.1/src/hlsync/progress.py +82 -0
  18. hlsync-1.20.1/src/hlsync/rules.py +240 -0
  19. hlsync-1.20.1/src/hlsync/selection.py +159 -0
  20. hlsync-1.20.1/src/hlsync/snapshot.py +196 -0
  21. hlsync-1.20.1/src/hlsync/storage.py +29 -0
  22. hlsync-1.20.1/src/hlsync/transfer.py +469 -0
  23. hlsync-1.20.1/src/hlsync/transport.py +861 -0
  24. hlsync-1.20.1/src/hlsync.egg-info/PKG-INFO +163 -0
  25. hlsync-1.20.1/src/hlsync.egg-info/SOURCES.txt +35 -0
  26. hlsync-1.20.1/src/hlsync.egg-info/dependency_links.txt +1 -0
  27. hlsync-1.20.1/src/hlsync.egg-info/entry_points.txt +2 -0
  28. hlsync-1.20.1/src/hlsync.egg-info/requires.txt +9 -0
  29. hlsync-1.20.1/src/hlsync.egg-info/top_level.txt +1 -0
  30. hlsync-1.20.1/tests/test_cli.py +1953 -0
  31. hlsync-1.20.1/tests/test_comparison.py +179 -0
  32. hlsync-1.20.1/tests/test_config.py +78 -0
  33. hlsync-1.20.1/tests/test_gitignore.py +33 -0
  34. hlsync-1.20.1/tests/test_progress.py +26 -0
  35. hlsync-1.20.1/tests/test_transport.py +1067 -0
  36. hlsync-1.20.1/tests/test_versioning.py +101 -0
  37. hlsync-1.20.1/version.json +4 -0
hlsync-1.20.1/LICENSE ADDED
@@ -0,0 +1,106 @@
1
+ Required Notice: Copyright (c) 2026 HLSync contributors (https://github.com/ediril/hlsync)
2
+
3
+ # PolyForm Strict License 1.0.0
4
+
5
+ <https://polyformproject.org/licenses/strict/1.0.0>
6
+
7
+ ## Acceptance
8
+
9
+ In order to get any license under these terms, you must agree
10
+ to them as both strict obligations and conditions to all
11
+ your licenses.
12
+
13
+ ## Copyright License
14
+
15
+ The licensor grants you a copyright license for the software
16
+ to do everything you might do with the software that would
17
+ otherwise infringe the licensor's copyright in it for any
18
+ permitted purpose, other than distributing the software or
19
+ making changes or new works based on the software.
20
+
21
+ ## Patent License
22
+
23
+ The licensor grants you a patent license for the software that
24
+ covers patent claims the licensor can license, or becomes able
25
+ to license, that you would infringe by using the software.
26
+
27
+ ## Noncommercial Purposes
28
+
29
+ Any noncommercial purpose is a permitted purpose.
30
+
31
+ ## Personal Uses
32
+
33
+ Personal use for research, experiment, and testing for
34
+ the benefit of public knowledge, personal study, private
35
+ entertainment, hobby projects, amateur pursuits, or religious
36
+ observance, without any anticipated commercial application,
37
+ is use for a permitted purpose.
38
+
39
+ ## Noncommercial Organizations
40
+
41
+ Use by any charitable organization, educational institution,
42
+ public research organization, public safety or health
43
+ organization, environmental protection organization,
44
+ or government institution is use for a permitted purpose
45
+ regardless of the source of funding or obligations resulting
46
+ from the funding.
47
+
48
+ ## Fair Use
49
+
50
+ You may have "fair use" rights for the software under the
51
+ law. These terms do not limit them.
52
+
53
+ ## No Other Rights
54
+
55
+ These terms do not allow you to sublicense or transfer any of
56
+ your licenses to anyone else, or prevent the licensor from
57
+ granting licenses to anyone else. These terms do not imply
58
+ any other licenses.
59
+
60
+ ## Patent Defense
61
+
62
+ If you make any written claim that the software infringes or
63
+ contributes to infringement of any patent, your patent license
64
+ for the software granted under these terms ends immediately. If
65
+ your company makes such a claim, your patent license ends
66
+ immediately for work on behalf of your company.
67
+
68
+ ## Violations
69
+
70
+ The first time you are notified in writing that you have
71
+ violated any of these terms, or done anything with the software
72
+ not covered by your licenses, your licenses can nonetheless
73
+ continue if you come into full compliance with these terms,
74
+ and take practical steps to correct past violations, within
75
+ 32 days of receiving notice. Otherwise, all your licenses
76
+ end immediately.
77
+
78
+ ## No Liability
79
+
80
+ ***As far as the law allows, the software comes as is, without
81
+ any warranty or condition, and the licensor will not be liable
82
+ to you for any damages arising out of these terms or the use
83
+ or nature of the software, under any kind of legal claim.***
84
+
85
+ ## Definitions
86
+
87
+ The **licensor** is the individual or entity offering these
88
+ terms, and the **software** is the software the licensor makes
89
+ available under these terms.
90
+
91
+ **You** refers to the individual or entity agreeing to these
92
+ terms.
93
+
94
+ **Your company** is any legal entity, sole proprietorship,
95
+ or other kind of organization that you work for, plus all
96
+ organizations that have control over, are under the control of,
97
+ or are under common control with that organization. **Control**
98
+ means ownership of substantially all the assets of an entity,
99
+ or the power to direct its management and policies by vote,
100
+ contract, or otherwise. Control can be direct or indirect.
101
+
102
+ **Your licenses** are all the licenses granted to you for the
103
+ software under these terms.
104
+
105
+ **Use** means anything you do with the software requiring one
106
+ of your licenses.
@@ -0,0 +1,3 @@
1
+ include version.json
2
+ include build_backend/*.py
3
+ include src/hlsync/_version.py
hlsync-1.20.1/PKG-INFO ADDED
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.4
2
+ Name: hlsync
3
+ Version: 1.20.1
4
+ Summary: Synchronize local files with remote servers over explicit FTPS
5
+ Author: HLSync contributors
6
+ License-Expression: LicenseRef-PolyForm-Strict-1.0.0
7
+ Project-URL: Homepage, https://github.com/ediril/hlsync
8
+ Project-URL: Changelog, https://github.com/ediril/hlsync/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/ediril/hlsync/issues
10
+ Project-URL: Repository, https://github.com/ediril/hlsync.git
11
+ Keywords: deployment,ftp,ftps,synchronization
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: System :: Archiving :: Backup
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: pathspec<2,>=0.12.1
26
+ Provides-Extra: dev
27
+ Requires-Dist: build<2,>=1; extra == "dev"
28
+ Requires-Dist: pyftpdlib[ssl]<3,>=2.0; extra == "dev"
29
+ Requires-Dist: pytest<10,>=8; extra == "dev"
30
+ Requires-Dist: ruff<1,>=0.12; extra == "dev"
31
+ Requires-Dist: setuptools>=77; extra == "dev"
32
+ Requires-Dist: twine<7,>=6; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # HLSync
36
+
37
+ HLSync (`hlsync`) is a command-line workflow for deploying local
38
+ projects to shared hosting over explicit FTP over TLS (FTPS): map once, preview
39
+ the diff, then push the intended files.
40
+
41
+ HLSync is pre-alpha software. Commands and configuration may change before a
42
+ stable release.
43
+
44
+ ## Who is it for?
45
+
46
+ HLSync is for people who still deploy websites or small projects to shared hosting over FTP.
47
+
48
+ If your current process is basically “figure out which files changed and upload them,” HLSync gives you a safer, more repeatable version of that workflow without requiring a full deployment system.
49
+
50
+ It works best when your local project is the source of truth and the remote server is simply where the project gets deployed.
51
+
52
+ ## Installation
53
+
54
+ HLSync requires Python 3.10 or newer. Install it from PyPI as an isolated
55
+ command-line tool with uv:
56
+
57
+ ```console
58
+ uv tool install hlsync
59
+ ```
60
+
61
+ Upgrade later with:
62
+
63
+ ```console
64
+ uv tool upgrade hlsync
65
+ ```
66
+
67
+ The FTPS server must support explicit TLS with protected data connections
68
+ (`AUTH TLS` and `PROT P`). HLSync prefers MLSD listings; servers without MLSD
69
+ use Unix-style LIST listings with SIZE and MDTM for exact file metadata.
70
+ LIST mode includes dotfiles and requires two extra requests per file. Push additionally
71
+ requires MFMT (or writable MDTM on vsFTPd) and MDTM read-back to apply and verify
72
+ uploaded-file timestamps.
73
+
74
+ ## Quick start
75
+
76
+ HLSync reads credentials from environment variables. The defaults are:
77
+
78
+ ```text
79
+ PROD_FTPS_USERNAME
80
+ PROD_FTPS_PASSWORD
81
+ ```
82
+
83
+ From the local project root:
84
+
85
+ ```console
86
+ hlsync create prod --host ftp.example.com --remote-root /public_html/site
87
+ hlsync connect
88
+ hlsync exclude node_modules
89
+ hlsync diff
90
+ hlsync push
91
+ ```
92
+
93
+ `create` proposes the current directory as the local root; pressing Enter accepts
94
+ it. Declining prompts for another local folder. Use `--local-root PATH` to
95
+ provide one directly. Both `diff` and `push` recurse by default. Use `-s` /
96
+ `--shallow` to process only the selected folder's immediate contents.
97
+
98
+ ## Everyday commands
99
+
100
+ ```console
101
+ hlsync profiles # list profiles; * marks the current one
102
+ hlsync exclude node_modules # keep a path out of deployments
103
+ hlsync include 'static/*.webp' # bring back Git-ignored files
104
+ hlsync rules # show rules and applied .gitignore patterns
105
+ hlsync list -r # local tree with exclusions marked
106
+ hlsync diff # what push would change, read-only
107
+ hlsync push --dry # preview the exact push
108
+ hlsync push # upload new and changed files
109
+ hlsync pull index.html # replace a local file from the server
110
+ ```
111
+
112
+ HLSync honors `.gitignore` files automatically. Push never deletes remote files
113
+ unless you pass `-p` / `--prune`, and it stops before overwriting a file that is
114
+ newer on the server. Each upload is staged beside its destination and verified
115
+ before it replaces the live file.
116
+
117
+ ## Documentation
118
+
119
+ - [Profiles and mapping](https://github.com/ediril/hlsync/blob/main/docs/profiles.md):
120
+ profiles, credentials, local and remote roots, and running commands from
121
+ anywhere.
122
+ - [Synchronization rules](https://github.com/ediril/hlsync/blob/main/docs/rules.md):
123
+ `.gitignore` handling, common and profile rules, wildcards, and remote
124
+ protection.
125
+ - [Paths, traversal, and listing](https://github.com/ediril/hlsync/blob/main/docs/paths-and-listing.md):
126
+ how paths select files, recursion depth, and `list`.
127
+ - [Diff](https://github.com/ediril/hlsync/blob/main/docs/diff.md):
128
+ status markers, pruning previews, and wider views.
129
+ - [Push and pull](https://github.com/ediril/hlsync/blob/main/docs/push-and-pull.md):
130
+ conflicts, deletion, progress, and safe file replacement.
131
+ - [Development and maintenance](https://github.com/ediril/hlsync/blob/main/docs/development.md):
132
+ testing local changes, switching installs, and releasing.
133
+
134
+ ## Command behavior
135
+
136
+ Commands accept the shortest unique prefix. Exact names win. An ambiguous
137
+ prefix prompts for a numbered choice on an interactive terminal and fails with
138
+ the candidate list in noninteractive use.
139
+
140
+ Use `hlsync help [command]`, `hlsync version`, and `hlsync --legend` for
141
+ built-in reference. `hlsync v`, `hlsync -v`, and `hlsync --version` also print
142
+ the installed version.
143
+
144
+ ## License
145
+
146
+ HLSync is source-available under the [PolyForm Strict License 1.0.0](https://github.com/ediril/hlsync/blob/main/LICENSE).
147
+ Use is free of charge for the purposes permitted by that license, including:
148
+
149
+ - Any noncommercial purpose.
150
+ - The personal uses listed in the license, such as personal study and hobby
151
+ projects, without any anticipated commercial application.
152
+ - Use by charitable organizations, educational institutions, public research
153
+ organizations, public safety or health organizations, environmental protection
154
+ organizations, and government institutions, regardless of funding source or
155
+ obligations resulting from funding.
156
+
157
+ This license grants no rights to modify or redistribute HLSync or create works
158
+ based on it. The full license governs; this summary does not grant additional
159
+ permissions.
160
+
161
+ Commercial use outside those permissions requires a separate paid license.
162
+ Contact [Emrah Diril](mailto:emrah@diril.org) for pricing and a commercial
163
+ agreement before beginning such use.
@@ -0,0 +1,129 @@
1
+ # HLSync
2
+
3
+ HLSync (`hlsync`) is a command-line workflow for deploying local
4
+ projects to shared hosting over explicit FTP over TLS (FTPS): map once, preview
5
+ the diff, then push the intended files.
6
+
7
+ HLSync is pre-alpha software. Commands and configuration may change before a
8
+ stable release.
9
+
10
+ ## Who is it for?
11
+
12
+ HLSync is for people who still deploy websites or small projects to shared hosting over FTP.
13
+
14
+ If your current process is basically “figure out which files changed and upload them,” HLSync gives you a safer, more repeatable version of that workflow without requiring a full deployment system.
15
+
16
+ It works best when your local project is the source of truth and the remote server is simply where the project gets deployed.
17
+
18
+ ## Installation
19
+
20
+ HLSync requires Python 3.10 or newer. Install it from PyPI as an isolated
21
+ command-line tool with uv:
22
+
23
+ ```console
24
+ uv tool install hlsync
25
+ ```
26
+
27
+ Upgrade later with:
28
+
29
+ ```console
30
+ uv tool upgrade hlsync
31
+ ```
32
+
33
+ The FTPS server must support explicit TLS with protected data connections
34
+ (`AUTH TLS` and `PROT P`). HLSync prefers MLSD listings; servers without MLSD
35
+ use Unix-style LIST listings with SIZE and MDTM for exact file metadata.
36
+ LIST mode includes dotfiles and requires two extra requests per file. Push additionally
37
+ requires MFMT (or writable MDTM on vsFTPd) and MDTM read-back to apply and verify
38
+ uploaded-file timestamps.
39
+
40
+ ## Quick start
41
+
42
+ HLSync reads credentials from environment variables. The defaults are:
43
+
44
+ ```text
45
+ PROD_FTPS_USERNAME
46
+ PROD_FTPS_PASSWORD
47
+ ```
48
+
49
+ From the local project root:
50
+
51
+ ```console
52
+ hlsync create prod --host ftp.example.com --remote-root /public_html/site
53
+ hlsync connect
54
+ hlsync exclude node_modules
55
+ hlsync diff
56
+ hlsync push
57
+ ```
58
+
59
+ `create` proposes the current directory as the local root; pressing Enter accepts
60
+ it. Declining prompts for another local folder. Use `--local-root PATH` to
61
+ provide one directly. Both `diff` and `push` recurse by default. Use `-s` /
62
+ `--shallow` to process only the selected folder's immediate contents.
63
+
64
+ ## Everyday commands
65
+
66
+ ```console
67
+ hlsync profiles # list profiles; * marks the current one
68
+ hlsync exclude node_modules # keep a path out of deployments
69
+ hlsync include 'static/*.webp' # bring back Git-ignored files
70
+ hlsync rules # show rules and applied .gitignore patterns
71
+ hlsync list -r # local tree with exclusions marked
72
+ hlsync diff # what push would change, read-only
73
+ hlsync push --dry # preview the exact push
74
+ hlsync push # upload new and changed files
75
+ hlsync pull index.html # replace a local file from the server
76
+ ```
77
+
78
+ HLSync honors `.gitignore` files automatically. Push never deletes remote files
79
+ unless you pass `-p` / `--prune`, and it stops before overwriting a file that is
80
+ newer on the server. Each upload is staged beside its destination and verified
81
+ before it replaces the live file.
82
+
83
+ ## Documentation
84
+
85
+ - [Profiles and mapping](https://github.com/ediril/hlsync/blob/main/docs/profiles.md):
86
+ profiles, credentials, local and remote roots, and running commands from
87
+ anywhere.
88
+ - [Synchronization rules](https://github.com/ediril/hlsync/blob/main/docs/rules.md):
89
+ `.gitignore` handling, common and profile rules, wildcards, and remote
90
+ protection.
91
+ - [Paths, traversal, and listing](https://github.com/ediril/hlsync/blob/main/docs/paths-and-listing.md):
92
+ how paths select files, recursion depth, and `list`.
93
+ - [Diff](https://github.com/ediril/hlsync/blob/main/docs/diff.md):
94
+ status markers, pruning previews, and wider views.
95
+ - [Push and pull](https://github.com/ediril/hlsync/blob/main/docs/push-and-pull.md):
96
+ conflicts, deletion, progress, and safe file replacement.
97
+ - [Development and maintenance](https://github.com/ediril/hlsync/blob/main/docs/development.md):
98
+ testing local changes, switching installs, and releasing.
99
+
100
+ ## Command behavior
101
+
102
+ Commands accept the shortest unique prefix. Exact names win. An ambiguous
103
+ prefix prompts for a numbered choice on an interactive terminal and fails with
104
+ the candidate list in noninteractive use.
105
+
106
+ Use `hlsync help [command]`, `hlsync version`, and `hlsync --legend` for
107
+ built-in reference. `hlsync v`, `hlsync -v`, and `hlsync --version` also print
108
+ the installed version.
109
+
110
+ ## License
111
+
112
+ HLSync is source-available under the [PolyForm Strict License 1.0.0](https://github.com/ediril/hlsync/blob/main/LICENSE).
113
+ Use is free of charge for the purposes permitted by that license, including:
114
+
115
+ - Any noncommercial purpose.
116
+ - The personal uses listed in the license, such as personal study and hobby
117
+ projects, without any anticipated commercial application.
118
+ - Use by charitable organizations, educational institutions, public research
119
+ organizations, public safety or health organizations, environmental protection
120
+ organizations, and government institutions, regardless of funding source or
121
+ obligations resulting from funding.
122
+
123
+ This license grants no rights to modify or redistribute HLSync or create works
124
+ based on it. The full license governs; this summary does not grant additional
125
+ permissions.
126
+
127
+ Commercial use outside those permissions requires a separate paid license.
128
+ Contact [Emrah Diril](mailto:emrah@diril.org) for pricing and a commercial
129
+ agreement before beginning such use.
@@ -0,0 +1,32 @@
1
+ """Setuptools build hooks that freeze the Git version into every artifact."""
2
+
3
+ from functools import wraps
4
+ from pathlib import Path
5
+
6
+ from setuptools import build_meta
7
+ from versioning import write_version
8
+
9
+ ROOT = Path(__file__).resolve().parents[1]
10
+
11
+
12
+ def versioned(hook):
13
+ @wraps(hook)
14
+ def run(*args, **kwargs):
15
+ write_version(ROOT)
16
+ return hook(*args, **kwargs)
17
+
18
+ return run
19
+
20
+
21
+ get_requires_for_build_wheel = versioned(build_meta.get_requires_for_build_wheel)
22
+ get_requires_for_build_sdist = versioned(build_meta.get_requires_for_build_sdist)
23
+ get_requires_for_build_editable = versioned(build_meta.get_requires_for_build_editable)
24
+ prepare_metadata_for_build_wheel = versioned(
25
+ build_meta.prepare_metadata_for_build_wheel
26
+ )
27
+ prepare_metadata_for_build_editable = versioned(
28
+ build_meta.prepare_metadata_for_build_editable
29
+ )
30
+ build_wheel = versioned(build_meta.build_wheel)
31
+ build_sdist = versioned(build_meta.build_sdist)
32
+ build_editable = versioned(build_meta.build_editable)
@@ -0,0 +1,75 @@
1
+ """Advance a starting release version along its baseline's first-parent history."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+ import subprocess
8
+ from datetime import datetime
9
+ from email.parser import Parser
10
+ from pathlib import Path
11
+
12
+ VERSION_PATTERN = re.compile(r"[1-9][0-9]*\.(?:0|[1-9][0-9]*)\.[1-9][0-9]*")
13
+
14
+
15
+ def git_version(root: Path) -> str:
16
+ baseline = json.loads((root / "version.json").read_text())
17
+ version, commit = baseline["version"], baseline["commit"]
18
+ if (
19
+ not isinstance(version, str)
20
+ or VERSION_PATTERN.fullmatch(version) is None
21
+ or not isinstance(commit, str)
22
+ or re.fullmatch(r"[0-9a-f]{40}", commit) is None
23
+ ):
24
+ raise ValueError(
25
+ "version.json requires a valid starting version and a full commit SHA"
26
+ )
27
+ major, work_day, increment = map(int, version.split("."))
28
+ result = subprocess.run(
29
+ ["git", "log", "--first-parent", "--format=%H %cI", "HEAD"],
30
+ cwd=root,
31
+ capture_output=True,
32
+ text=True,
33
+ check=True,
34
+ )
35
+ dates = []
36
+ for line in result.stdout.splitlines():
37
+ sha, timestamp = line.split()
38
+ dates.append(datetime.fromisoformat(timestamp).date())
39
+ if sha == commit:
40
+ break
41
+ else:
42
+ raise ValueError(
43
+ "version baseline is missing from first-parent history; "
44
+ "fetch the full history or correct version.json"
45
+ )
46
+ if dates != sorted(dates, reverse=True):
47
+ raise ValueError("commit dates go backwards in the version baseline's history")
48
+ work_day += len(set(dates)) - 1
49
+ daily_commits = dates.count(dates[0])
50
+ if dates[0] == dates[-1]:
51
+ daily_commits += increment - 1
52
+ return f"{major}.{work_day}.{daily_commits}"
53
+
54
+
55
+ def release_version(root: Path) -> str:
56
+ if (root / ".git").exists():
57
+ return git_version(root)
58
+
59
+ # An sdist carries the version fixed when it was built, without needing Git.
60
+ metadata = root / "PKG-INFO"
61
+ if not metadata.is_file():
62
+ raise ValueError("version requires a Git checkout or a built source archive")
63
+ version = Parser().parsestr(metadata.read_text())["Version"]
64
+ if version is None or VERSION_PATTERN.fullmatch(version) is None:
65
+ raise ValueError("source archive has an invalid release version")
66
+ return version
67
+
68
+
69
+ def write_version(root: Path) -> str:
70
+ version = release_version(root)
71
+ path = root / "src" / "hlsync" / "_version.py"
72
+ content = f'# Generated at build time; do not edit.\n__version__ = "{version}"\n'
73
+ if not path.exists() or path.read_text() != content:
74
+ path.write_text(content)
75
+ return version
@@ -0,0 +1,71 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "hlsync_build"
4
+ backend-path = ["build_backend"]
5
+
6
+ [project]
7
+ name = "hlsync"
8
+ dynamic = ["version"]
9
+ description = "Synchronize local files with remote servers over explicit FTPS"
10
+ readme = "README.md"
11
+ license = "LicenseRef-PolyForm-Strict-1.0.0"
12
+ license-files = ["LICENSE"]
13
+ requires-python = ">=3.10"
14
+ dependencies = ["pathspec>=0.12.1,<2"]
15
+ authors = [{ name = "HLSync contributors" }]
16
+ keywords = ["deployment", "ftp", "ftps", "synchronization"]
17
+ classifiers = [
18
+ "Development Status :: 2 - Pre-Alpha",
19
+ "Environment :: Console",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
+ "Topic :: System :: Archiving :: Backup",
28
+ ]
29
+ [project.urls]
30
+ Homepage = "https://github.com/ediril/hlsync"
31
+ Changelog = "https://github.com/ediril/hlsync/blob/main/CHANGELOG.md"
32
+ Issues = "https://github.com/ediril/hlsync/issues"
33
+ Repository = "https://github.com/ediril/hlsync.git"
34
+
35
+ [project.optional-dependencies]
36
+ dev = [
37
+ "build>=1,<2",
38
+ "pyftpdlib[ssl]>=2.0,<3",
39
+ "pytest>=8,<10",
40
+ "ruff>=0.12,<1",
41
+ "setuptools>=77",
42
+ "twine>=6,<7",
43
+ ]
44
+
45
+ [project.scripts]
46
+ hlsync = "hlsync.cli:main"
47
+
48
+ [tool.setuptools.dynamic]
49
+ version = { attr = "hlsync._version.__version__" }
50
+
51
+ [tool.setuptools.packages.find]
52
+ where = ["src"]
53
+
54
+ [tool.uv]
55
+ cache-keys = [
56
+ { file = "pyproject.toml" },
57
+ { file = "version.json" },
58
+ { file = "build_backend/*.py" },
59
+ { git = { commit = true } },
60
+ ]
61
+
62
+ [tool.pytest.ini_options]
63
+ addopts = "-ra"
64
+ testpaths = ["tests"]
65
+
66
+ [tool.ruff]
67
+ target-version = "py310"
68
+ line-length = 88
69
+
70
+ [tool.ruff.lint]
71
+ select = ["E", "F", "I", "UP"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """HLSync."""
2
+
3
+ from ._version import __version__
4
+
5
+ __all__ = ["__version__"]
@@ -0,0 +1,4 @@
1
+ from hlsync.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,2 @@
1
+ # Generated at build time; do not edit.
2
+ __version__ = "1.20.1"