hlsync 1.20.3__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 (39) hide show
  1. hlsync-1.20.3/LICENSE +106 -0
  2. hlsync-1.20.3/MANIFEST.in +3 -0
  3. hlsync-1.20.3/PKG-INFO +166 -0
  4. hlsync-1.20.3/README.md +132 -0
  5. hlsync-1.20.3/build_backend/hlsync_build.py +32 -0
  6. hlsync-1.20.3/build_backend/versioning.py +75 -0
  7. hlsync-1.20.3/pyproject.toml +71 -0
  8. hlsync-1.20.3/setup.cfg +4 -0
  9. hlsync-1.20.3/src/hlsync/__init__.py +5 -0
  10. hlsync-1.20.3/src/hlsync/__main__.py +4 -0
  11. hlsync-1.20.3/src/hlsync/_version.py +2 -0
  12. hlsync-1.20.3/src/hlsync/cli.py +3215 -0
  13. hlsync-1.20.3/src/hlsync/comparison.py +278 -0
  14. hlsync-1.20.3/src/hlsync/config.py +584 -0
  15. hlsync-1.20.3/src/hlsync/gitignore.py +98 -0
  16. hlsync-1.20.3/src/hlsync/pattern_operands.py +33 -0
  17. hlsync-1.20.3/src/hlsync/progress.py +82 -0
  18. hlsync-1.20.3/src/hlsync/rules.py +240 -0
  19. hlsync-1.20.3/src/hlsync/selection.py +159 -0
  20. hlsync-1.20.3/src/hlsync/shell.py +78 -0
  21. hlsync-1.20.3/src/hlsync/snapshot.py +196 -0
  22. hlsync-1.20.3/src/hlsync/storage.py +34 -0
  23. hlsync-1.20.3/src/hlsync/transfer.py +469 -0
  24. hlsync-1.20.3/src/hlsync/transport.py +861 -0
  25. hlsync-1.20.3/src/hlsync.egg-info/PKG-INFO +166 -0
  26. hlsync-1.20.3/src/hlsync.egg-info/SOURCES.txt +37 -0
  27. hlsync-1.20.3/src/hlsync.egg-info/dependency_links.txt +1 -0
  28. hlsync-1.20.3/src/hlsync.egg-info/entry_points.txt +2 -0
  29. hlsync-1.20.3/src/hlsync.egg-info/requires.txt +9 -0
  30. hlsync-1.20.3/src/hlsync.egg-info/top_level.txt +1 -0
  31. hlsync-1.20.3/tests/test_cli.py +1989 -0
  32. hlsync-1.20.3/tests/test_comparison.py +179 -0
  33. hlsync-1.20.3/tests/test_config.py +78 -0
  34. hlsync-1.20.3/tests/test_gitignore.py +33 -0
  35. hlsync-1.20.3/tests/test_progress.py +26 -0
  36. hlsync-1.20.3/tests/test_shell.py +92 -0
  37. hlsync-1.20.3/tests/test_transport.py +1067 -0
  38. hlsync-1.20.3/tests/test_versioning.py +101 -0
  39. hlsync-1.20.3/version.json +4 -0
hlsync-1.20.3/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.3/PKG-INFO ADDED
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.4
2
+ Name: hlsync
3
+ Version: 1.20.3
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
+ Set `PROD_FTPS_HOST` too and `create` can omit `--host`.
84
+
85
+ From the local project root:
86
+
87
+ ```console
88
+ hlsync create prod --host ftp.example.com --remote-root /public_html/site
89
+ hlsync connect
90
+ hlsync exclude node_modules
91
+ hlsync diff
92
+ hlsync push
93
+ ```
94
+
95
+ `create` proposes the current directory as the local root; pressing Enter accepts
96
+ it. Declining prompts for another local folder. Use `--local-root PATH` to
97
+ provide one directly. Both `diff` and `push` recurse by default. Use `-s` /
98
+ `--shallow` to process only the selected folder's immediate contents.
99
+
100
+ ## Everyday commands
101
+
102
+ ```console
103
+ hlsync profiles # list profiles; * marks the current one
104
+ hlsync cd staging # go to a profile's folder (after hlsync setup)
105
+ hlsync exclude node_modules # keep a path out of deployments
106
+ hlsync include 'static/*.webp' # bring back Git-ignored files
107
+ hlsync rules # show rules and applied .gitignore patterns
108
+ hlsync list -r # local tree with exclusions marked
109
+ hlsync diff # what push would change, read-only
110
+ hlsync push --dry # preview the exact push
111
+ hlsync push # upload new and changed files
112
+ hlsync pull index.html # replace a local file from the server
113
+ ```
114
+
115
+ HLSync honors `.gitignore` files automatically. Push never deletes remote files
116
+ unless you pass `-p` / `--prune`, and it stops before overwriting a file that is
117
+ newer on the server. Each upload is staged beside its destination and verified
118
+ before it replaces the live file.
119
+
120
+ ## Documentation
121
+
122
+ - [Profiles and mapping](https://github.com/ediril/hlsync/blob/main/docs/profiles.md):
123
+ profiles, credentials, local and remote roots, and running commands from
124
+ anywhere.
125
+ - [Synchronization rules](https://github.com/ediril/hlsync/blob/main/docs/rules.md):
126
+ `.gitignore` handling, common and profile rules, wildcards, and remote
127
+ protection.
128
+ - [Paths, traversal, and listing](https://github.com/ediril/hlsync/blob/main/docs/paths-and-listing.md):
129
+ how paths select files, recursion depth, and `list`.
130
+ - [Diff](https://github.com/ediril/hlsync/blob/main/docs/diff.md):
131
+ status markers, pruning previews, and wider views.
132
+ - [Push and pull](https://github.com/ediril/hlsync/blob/main/docs/push-and-pull.md):
133
+ conflicts, deletion, progress, and safe file replacement.
134
+ - [Development and maintenance](https://github.com/ediril/hlsync/blob/main/docs/development.md):
135
+ testing local changes, switching installs, and releasing.
136
+
137
+ ## Command behavior
138
+
139
+ Commands accept the shortest unique prefix. Exact names win. An ambiguous
140
+ prefix prompts for a numbered choice on an interactive terminal and fails with
141
+ the candidate list in noninteractive use.
142
+
143
+ Use `hlsync help [command]`, `hlsync version`, and `hlsync --legend` for
144
+ built-in reference. `hlsync v`, `hlsync -v`, and `hlsync --version` also print
145
+ the installed version.
146
+
147
+ ## License
148
+
149
+ HLSync is source-available under the [PolyForm Strict License 1.0.0](https://github.com/ediril/hlsync/blob/main/LICENSE).
150
+ Use is free of charge for the purposes permitted by that license, including:
151
+
152
+ - Any noncommercial purpose.
153
+ - The personal uses listed in the license, such as personal study and hobby
154
+ projects, without any anticipated commercial application.
155
+ - Use by charitable organizations, educational institutions, public research
156
+ organizations, public safety or health organizations, environmental protection
157
+ organizations, and government institutions, regardless of funding source or
158
+ obligations resulting from funding.
159
+
160
+ This license grants no rights to modify or redistribute HLSync or create works
161
+ based on it. The full license governs; this summary does not grant additional
162
+ permissions.
163
+
164
+ Commercial use outside those permissions requires a separate paid license.
165
+ Contact [Emrah Diril](mailto:emrah@diril.org) for pricing and a commercial
166
+ agreement before beginning such use.
@@ -0,0 +1,132 @@
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
+ Set `PROD_FTPS_HOST` too and `create` can omit `--host`.
50
+
51
+ From the local project root:
52
+
53
+ ```console
54
+ hlsync create prod --host ftp.example.com --remote-root /public_html/site
55
+ hlsync connect
56
+ hlsync exclude node_modules
57
+ hlsync diff
58
+ hlsync push
59
+ ```
60
+
61
+ `create` proposes the current directory as the local root; pressing Enter accepts
62
+ it. Declining prompts for another local folder. Use `--local-root PATH` to
63
+ provide one directly. Both `diff` and `push` recurse by default. Use `-s` /
64
+ `--shallow` to process only the selected folder's immediate contents.
65
+
66
+ ## Everyday commands
67
+
68
+ ```console
69
+ hlsync profiles # list profiles; * marks the current one
70
+ hlsync cd staging # go to a profile's folder (after hlsync setup)
71
+ hlsync exclude node_modules # keep a path out of deployments
72
+ hlsync include 'static/*.webp' # bring back Git-ignored files
73
+ hlsync rules # show rules and applied .gitignore patterns
74
+ hlsync list -r # local tree with exclusions marked
75
+ hlsync diff # what push would change, read-only
76
+ hlsync push --dry # preview the exact push
77
+ hlsync push # upload new and changed files
78
+ hlsync pull index.html # replace a local file from the server
79
+ ```
80
+
81
+ HLSync honors `.gitignore` files automatically. Push never deletes remote files
82
+ unless you pass `-p` / `--prune`, and it stops before overwriting a file that is
83
+ newer on the server. Each upload is staged beside its destination and verified
84
+ before it replaces the live file.
85
+
86
+ ## Documentation
87
+
88
+ - [Profiles and mapping](https://github.com/ediril/hlsync/blob/main/docs/profiles.md):
89
+ profiles, credentials, local and remote roots, and running commands from
90
+ anywhere.
91
+ - [Synchronization rules](https://github.com/ediril/hlsync/blob/main/docs/rules.md):
92
+ `.gitignore` handling, common and profile rules, wildcards, and remote
93
+ protection.
94
+ - [Paths, traversal, and listing](https://github.com/ediril/hlsync/blob/main/docs/paths-and-listing.md):
95
+ how paths select files, recursion depth, and `list`.
96
+ - [Diff](https://github.com/ediril/hlsync/blob/main/docs/diff.md):
97
+ status markers, pruning previews, and wider views.
98
+ - [Push and pull](https://github.com/ediril/hlsync/blob/main/docs/push-and-pull.md):
99
+ conflicts, deletion, progress, and safe file replacement.
100
+ - [Development and maintenance](https://github.com/ediril/hlsync/blob/main/docs/development.md):
101
+ testing local changes, switching installs, and releasing.
102
+
103
+ ## Command behavior
104
+
105
+ Commands accept the shortest unique prefix. Exact names win. An ambiguous
106
+ prefix prompts for a numbered choice on an interactive terminal and fails with
107
+ the candidate list in noninteractive use.
108
+
109
+ Use `hlsync help [command]`, `hlsync version`, and `hlsync --legend` for
110
+ built-in reference. `hlsync v`, `hlsync -v`, and `hlsync --version` also print
111
+ the installed version.
112
+
113
+ ## License
114
+
115
+ HLSync is source-available under the [PolyForm Strict License 1.0.0](https://github.com/ediril/hlsync/blob/main/LICENSE).
116
+ Use is free of charge for the purposes permitted by that license, including:
117
+
118
+ - Any noncommercial purpose.
119
+ - The personal uses listed in the license, such as personal study and hobby
120
+ projects, without any anticipated commercial application.
121
+ - Use by charitable organizations, educational institutions, public research
122
+ organizations, public safety or health organizations, environmental protection
123
+ organizations, and government institutions, regardless of funding source or
124
+ obligations resulting from funding.
125
+
126
+ This license grants no rights to modify or redistribute HLSync or create works
127
+ based on it. The full license governs; this summary does not grant additional
128
+ permissions.
129
+
130
+ Commercial use outside those permissions requires a separate paid license.
131
+ Contact [Emrah Diril](mailto:emrah@diril.org) for pricing and a commercial
132
+ 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.3"