hlsync 1.19.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.
- hlsync-1.19.1/LICENSE +106 -0
- hlsync-1.19.1/MANIFEST.in +3 -0
- hlsync-1.19.1/PKG-INFO +163 -0
- hlsync-1.19.1/README.md +129 -0
- hlsync-1.19.1/build_backend/hlsync_build.py +32 -0
- hlsync-1.19.1/build_backend/versioning.py +75 -0
- hlsync-1.19.1/pyproject.toml +71 -0
- hlsync-1.19.1/setup.cfg +4 -0
- hlsync-1.19.1/src/hlsync/__init__.py +5 -0
- hlsync-1.19.1/src/hlsync/__main__.py +4 -0
- hlsync-1.19.1/src/hlsync/_version.py +2 -0
- hlsync-1.19.1/src/hlsync/cli.py +3038 -0
- hlsync-1.19.1/src/hlsync/comparison.py +278 -0
- hlsync-1.19.1/src/hlsync/config.py +583 -0
- hlsync-1.19.1/src/hlsync/gitignore.py +59 -0
- hlsync-1.19.1/src/hlsync/pattern_operands.py +33 -0
- hlsync-1.19.1/src/hlsync/progress.py +57 -0
- hlsync-1.19.1/src/hlsync/rules.py +240 -0
- hlsync-1.19.1/src/hlsync/selection.py +159 -0
- hlsync-1.19.1/src/hlsync/snapshot.py +196 -0
- hlsync-1.19.1/src/hlsync/storage.py +29 -0
- hlsync-1.19.1/src/hlsync/transfer.py +469 -0
- hlsync-1.19.1/src/hlsync/transport.py +764 -0
- hlsync-1.19.1/src/hlsync.egg-info/PKG-INFO +163 -0
- hlsync-1.19.1/src/hlsync.egg-info/SOURCES.txt +35 -0
- hlsync-1.19.1/src/hlsync.egg-info/dependency_links.txt +1 -0
- hlsync-1.19.1/src/hlsync.egg-info/entry_points.txt +2 -0
- hlsync-1.19.1/src/hlsync.egg-info/requires.txt +9 -0
- hlsync-1.19.1/src/hlsync.egg-info/top_level.txt +1 -0
- hlsync-1.19.1/tests/test_cli.py +1882 -0
- hlsync-1.19.1/tests/test_comparison.py +179 -0
- hlsync-1.19.1/tests/test_config.py +78 -0
- hlsync-1.19.1/tests/test_gitignore.py +33 -0
- hlsync-1.19.1/tests/test_progress.py +26 -0
- hlsync-1.19.1/tests/test_transport.py +989 -0
- hlsync-1.19.1/tests/test_versioning.py +101 -0
- hlsync-1.19.1/version.json +4 -0
hlsync-1.19.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.
|
hlsync-1.19.1/PKG-INFO
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hlsync
|
|
3
|
+
Version: 1.19.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 common and profile rules
|
|
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.
|
hlsync-1.19.1/README.md
ADDED
|
@@ -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 common and profile rules
|
|
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"]
|
hlsync-1.19.1/setup.cfg
ADDED