ddns 4.0.0b5__tar.gz → 4.0.0b7__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 ddns might be problematic. Click here for more details.

@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddns
3
- Version: 4.0.0b5
4
- Summary: automatically update DNS records to dynamic local IP [自动更新DNS记录指向本地IP]
3
+ Version: 4.0.0b7
4
+ Summary: automatically update DNS records to my IP [域名自动指向本机IP]
5
5
  Home-page: https://ddns.newfuture.cc
6
6
  Author: NewFuture
7
7
  Author-email: python@newfuture.cc
@@ -60,9 +60,9 @@ Dynamic: summary
60
60
  - [二进制文件](https://github.com/NewFuture/DDNS/releases/latest) ![cross platform](https://img.shields.io/badge/system-windows_%7C%20linux_%7C%20mac-success.svg?style=social)
61
61
 
62
62
  - 配置方式:
63
- - [命令行参数](#详细配置)
64
- - [JSON 配置文件](#详细配置)
65
- - [环境变量配置](doc/env.md) 📖
63
+ - [命令行参数](doc/cli.md)
64
+ - [JSON 配置文件](doc/json.md)
65
+ - [环境变量配置](doc/env.md)
66
66
 
67
67
  - 域名支持:
68
68
  - 多个域名支持
@@ -155,12 +155,27 @@ Dynamic: summary
155
155
 
156
156
  ## 详细配置
157
157
 
158
- 所有字段可通过三种方式进行配置
158
+ 所有字段可通过三种方式进行配置,优先级为:**命令行参数 > JSON配置文件 > 环境变量**
159
159
 
160
- 1. 命令行参数 `ddns --key=value`(`ddns -h` 查看详情),优先级最高
161
- 2. JSON 配置文件(值为 null 认为是有效值,会覆盖环境变量的设置,如果没有对应的 key 则会尝试使用环境变量)
160
+ 1. [命令行参数](doc/cli.md) `ddns --key=value`(`ddns -h` 查看详情),优先级最高
161
+ 2. [JSON 配置文件](doc/json.md)(值为 null 认为是有效值,会覆盖环境变量的设置,如果没有对应的 key 则会尝试使用环境变量)
162
162
  3. 环境变量 DDNS_ 前缀加上 key 全大写或者全小写,点转下划线(`${ddns_id}` 或 `${DDNS_ID}`,`${DDNS_LOG_LEVEL}`)
163
163
 
164
+ ### 配置优先级和字段覆盖关系
165
+
166
+ 如果同一个配置项在多个地方设置,将按照以下优先级规则生效:
167
+
168
+ - **命令行参数**:优先级最高,会覆盖其他所有设置
169
+ - **JSON配置文件**:介于命令行和环境变量之间,会覆盖环境变量中的设置
170
+ - **环境变量**:优先级最低,当其他方式未设置时使用
171
+
172
+ **特殊情况**:
173
+ - JSON配置中明确设为`null`的值会覆盖环境变量设置
174
+ - `debug`参数只在命令行中有效,JSON配置文件中的同名设置无效
175
+ - 多值参数(如`ipv4`、`ipv6`等)在命令行中使用方式为重复使用参数,如`--ipv4 domain1 --ipv4 domain2`
176
+
177
+ 各配置方式的详细说明请查看对应文档:[命令行](doc/cli.md)、[JSON配置](doc/json.md)、[环境变量](doc/env.md)
178
+
164
179
  > 📖 **环境变量详细配置**: 查看 [环境变量配置文档](doc/env.md) 了解所有环境变量的详细用法和示例
165
180
 
166
181
  <details open>
@@ -169,6 +184,7 @@ Dynamic: summary
169
184
  - 首次运行会自动生成一个模板配置文件
170
185
  - 可以使用 `-c` 使用指定的配置文件(默认读取当前目录的 config.json)
171
186
  - 推荐使用 vscode 等支持 JsonSchema 的编辑器编辑配置文件
187
+ - 查看 [JSON配置文件详细文档](doc/json.md) 了解完整的配置选项和示例
172
188
 
173
189
  ```bash
174
190
  ddns -c path/to/config.json
@@ -189,9 +205,9 @@ python run.py -c /path/to/config.json
189
205
  | index6 | string\|int\|array | No | `"default"` | ipv6 获取方式 | 可设置 `网卡`、`内网`、`公网`、`正则` 等方式 |
190
206
  | ttl | number | No | `null` | DNS 解析 TTL 时间 | 不设置采用 DNS 默认策略 |
191
207
  | proxy | string\|array | No | 无 | http 代理 `;` 分割 | 多代理逐个尝试直到成功,`DIRECT` 为直连 |
192
- | ~~debug~~| bool | No | `false` | 是否开启调试 | v4 中弃用,请改用 log.level=DEBUG |
208
+ | debug | bool | No | `false` | 是否开启调试 | 等同于设置 log.level=DEBUG,仅命令行参数`--debug`有效 |
193
209
  | cache | string\|bool | No | `true` | 是否缓存记录 | 正常情况打开避免频繁更新,默认位置为临时目录下 `ddns.cache`,也可以指定一个具体路径 |
194
- | log | {"level":string,"file":string} | No | `null` | 日志配置(可选) | 日志配置,日志级别和路径(默认命令行),例如:`{ "level": "DEBUG", "file": "dns.log" }` |
210
+ | log | object | No | `null` | 日志配置(可选) | 日志配置对象,支持`level`、`file`、`format`、`datefmt`参数 |
195
211
 
196
212
  #### index4 和 index6 参数说明
197
213
 
@@ -237,7 +253,9 @@ python run.py -c /path/to/config.json
237
253
  "proxy": "127.0.0.1:1080;DIRECT",
238
254
  "log": {
239
255
  "level": "DEBUG",
240
- "file": "dns.log"
256
+ "file": "dns.log",
257
+ "format": "%(asctime)s %(levelname)s [%(module)s]: %(message)s",
258
+ "datefmt": "%Y-%m-%dT%H:%M:%S"
241
259
  }
242
260
  }
243
261
  ```
@@ -19,9 +19,9 @@
19
19
  - [二进制文件](https://github.com/NewFuture/DDNS/releases/latest) ![cross platform](https://img.shields.io/badge/system-windows_%7C%20linux_%7C%20mac-success.svg?style=social)
20
20
 
21
21
  - 配置方式:
22
- - [命令行参数](#详细配置)
23
- - [JSON 配置文件](#详细配置)
24
- - [环境变量配置](doc/env.md) 📖
22
+ - [命令行参数](doc/cli.md)
23
+ - [JSON 配置文件](doc/json.md)
24
+ - [环境变量配置](doc/env.md)
25
25
 
26
26
  - 域名支持:
27
27
  - 多个域名支持
@@ -114,12 +114,27 @@
114
114
 
115
115
  ## 详细配置
116
116
 
117
- 所有字段可通过三种方式进行配置
117
+ 所有字段可通过三种方式进行配置,优先级为:**命令行参数 > JSON配置文件 > 环境变量**
118
118
 
119
- 1. 命令行参数 `ddns --key=value`(`ddns -h` 查看详情),优先级最高
120
- 2. JSON 配置文件(值为 null 认为是有效值,会覆盖环境变量的设置,如果没有对应的 key 则会尝试使用环境变量)
119
+ 1. [命令行参数](doc/cli.md) `ddns --key=value`(`ddns -h` 查看详情),优先级最高
120
+ 2. [JSON 配置文件](doc/json.md)(值为 null 认为是有效值,会覆盖环境变量的设置,如果没有对应的 key 则会尝试使用环境变量)
121
121
  3. 环境变量 DDNS_ 前缀加上 key 全大写或者全小写,点转下划线(`${ddns_id}` 或 `${DDNS_ID}`,`${DDNS_LOG_LEVEL}`)
122
122
 
123
+ ### 配置优先级和字段覆盖关系
124
+
125
+ 如果同一个配置项在多个地方设置,将按照以下优先级规则生效:
126
+
127
+ - **命令行参数**:优先级最高,会覆盖其他所有设置
128
+ - **JSON配置文件**:介于命令行和环境变量之间,会覆盖环境变量中的设置
129
+ - **环境变量**:优先级最低,当其他方式未设置时使用
130
+
131
+ **特殊情况**:
132
+ - JSON配置中明确设为`null`的值会覆盖环境变量设置
133
+ - `debug`参数只在命令行中有效,JSON配置文件中的同名设置无效
134
+ - 多值参数(如`ipv4`、`ipv6`等)在命令行中使用方式为重复使用参数,如`--ipv4 domain1 --ipv4 domain2`
135
+
136
+ 各配置方式的详细说明请查看对应文档:[命令行](doc/cli.md)、[JSON配置](doc/json.md)、[环境变量](doc/env.md)
137
+
123
138
  > 📖 **环境变量详细配置**: 查看 [环境变量配置文档](doc/env.md) 了解所有环境变量的详细用法和示例
124
139
 
125
140
  <details open>
@@ -128,6 +143,7 @@
128
143
  - 首次运行会自动生成一个模板配置文件
129
144
  - 可以使用 `-c` 使用指定的配置文件(默认读取当前目录的 config.json)
130
145
  - 推荐使用 vscode 等支持 JsonSchema 的编辑器编辑配置文件
146
+ - 查看 [JSON配置文件详细文档](doc/json.md) 了解完整的配置选项和示例
131
147
 
132
148
  ```bash
133
149
  ddns -c path/to/config.json
@@ -148,9 +164,9 @@ python run.py -c /path/to/config.json
148
164
  | index6 | string\|int\|array | No | `"default"` | ipv6 获取方式 | 可设置 `网卡`、`内网`、`公网`、`正则` 等方式 |
149
165
  | ttl | number | No | `null` | DNS 解析 TTL 时间 | 不设置采用 DNS 默认策略 |
150
166
  | proxy | string\|array | No | 无 | http 代理 `;` 分割 | 多代理逐个尝试直到成功,`DIRECT` 为直连 |
151
- | ~~debug~~| bool | No | `false` | 是否开启调试 | v4 中弃用,请改用 log.level=DEBUG |
167
+ | debug | bool | No | `false` | 是否开启调试 | 等同于设置 log.level=DEBUG,仅命令行参数`--debug`有效 |
152
168
  | cache | string\|bool | No | `true` | 是否缓存记录 | 正常情况打开避免频繁更新,默认位置为临时目录下 `ddns.cache`,也可以指定一个具体路径 |
153
- | log | {"level":string,"file":string} | No | `null` | 日志配置(可选) | 日志配置,日志级别和路径(默认命令行),例如:`{ "level": "DEBUG", "file": "dns.log" }` |
169
+ | log | object | No | `null` | 日志配置(可选) | 日志配置对象,支持`level`、`file`、`format`、`datefmt`参数 |
154
170
 
155
171
  #### index4 和 index6 参数说明
156
172
 
@@ -196,7 +212,9 @@ python run.py -c /path/to/config.json
196
212
  "proxy": "127.0.0.1:1080;DIRECT",
197
213
  "log": {
198
214
  "level": "DEBUG",
199
- "file": "dns.log"
215
+ "file": "dns.log",
216
+ "format": "%(asctime)s %(levelname)s [%(module)s]: %(message)s",
217
+ "datefmt": "%Y-%m-%dT%H:%M:%S"
200
218
  }
201
219
  }
202
220
  ```
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddns
3
- Version: 4.0.0b5
4
- Summary: automatically update DNS records to dynamic local IP [自动更新DNS记录指向本地IP]
3
+ Version: 4.0.0b7
4
+ Summary: automatically update DNS records to my IP [域名自动指向本机IP]
5
5
  Home-page: https://ddns.newfuture.cc
6
6
  Author: NewFuture
7
7
  Author-email: python@newfuture.cc
@@ -60,9 +60,9 @@ Dynamic: summary
60
60
  - [二进制文件](https://github.com/NewFuture/DDNS/releases/latest) ![cross platform](https://img.shields.io/badge/system-windows_%7C%20linux_%7C%20mac-success.svg?style=social)
61
61
 
62
62
  - 配置方式:
63
- - [命令行参数](#详细配置)
64
- - [JSON 配置文件](#详细配置)
65
- - [环境变量配置](doc/env.md) 📖
63
+ - [命令行参数](doc/cli.md)
64
+ - [JSON 配置文件](doc/json.md)
65
+ - [环境变量配置](doc/env.md)
66
66
 
67
67
  - 域名支持:
68
68
  - 多个域名支持
@@ -155,12 +155,27 @@ Dynamic: summary
155
155
 
156
156
  ## 详细配置
157
157
 
158
- 所有字段可通过三种方式进行配置
158
+ 所有字段可通过三种方式进行配置,优先级为:**命令行参数 > JSON配置文件 > 环境变量**
159
159
 
160
- 1. 命令行参数 `ddns --key=value`(`ddns -h` 查看详情),优先级最高
161
- 2. JSON 配置文件(值为 null 认为是有效值,会覆盖环境变量的设置,如果没有对应的 key 则会尝试使用环境变量)
160
+ 1. [命令行参数](doc/cli.md) `ddns --key=value`(`ddns -h` 查看详情),优先级最高
161
+ 2. [JSON 配置文件](doc/json.md)(值为 null 认为是有效值,会覆盖环境变量的设置,如果没有对应的 key 则会尝试使用环境变量)
162
162
  3. 环境变量 DDNS_ 前缀加上 key 全大写或者全小写,点转下划线(`${ddns_id}` 或 `${DDNS_ID}`,`${DDNS_LOG_LEVEL}`)
163
163
 
164
+ ### 配置优先级和字段覆盖关系
165
+
166
+ 如果同一个配置项在多个地方设置,将按照以下优先级规则生效:
167
+
168
+ - **命令行参数**:优先级最高,会覆盖其他所有设置
169
+ - **JSON配置文件**:介于命令行和环境变量之间,会覆盖环境变量中的设置
170
+ - **环境变量**:优先级最低,当其他方式未设置时使用
171
+
172
+ **特殊情况**:
173
+ - JSON配置中明确设为`null`的值会覆盖环境变量设置
174
+ - `debug`参数只在命令行中有效,JSON配置文件中的同名设置无效
175
+ - 多值参数(如`ipv4`、`ipv6`等)在命令行中使用方式为重复使用参数,如`--ipv4 domain1 --ipv4 domain2`
176
+
177
+ 各配置方式的详细说明请查看对应文档:[命令行](doc/cli.md)、[JSON配置](doc/json.md)、[环境变量](doc/env.md)
178
+
164
179
  > 📖 **环境变量详细配置**: 查看 [环境变量配置文档](doc/env.md) 了解所有环境变量的详细用法和示例
165
180
 
166
181
  <details open>
@@ -169,6 +184,7 @@ Dynamic: summary
169
184
  - 首次运行会自动生成一个模板配置文件
170
185
  - 可以使用 `-c` 使用指定的配置文件(默认读取当前目录的 config.json)
171
186
  - 推荐使用 vscode 等支持 JsonSchema 的编辑器编辑配置文件
187
+ - 查看 [JSON配置文件详细文档](doc/json.md) 了解完整的配置选项和示例
172
188
 
173
189
  ```bash
174
190
  ddns -c path/to/config.json
@@ -189,9 +205,9 @@ python run.py -c /path/to/config.json
189
205
  | index6 | string\|int\|array | No | `"default"` | ipv6 获取方式 | 可设置 `网卡`、`内网`、`公网`、`正则` 等方式 |
190
206
  | ttl | number | No | `null` | DNS 解析 TTL 时间 | 不设置采用 DNS 默认策略 |
191
207
  | proxy | string\|array | No | 无 | http 代理 `;` 分割 | 多代理逐个尝试直到成功,`DIRECT` 为直连 |
192
- | ~~debug~~| bool | No | `false` | 是否开启调试 | v4 中弃用,请改用 log.level=DEBUG |
208
+ | debug | bool | No | `false` | 是否开启调试 | 等同于设置 log.level=DEBUG,仅命令行参数`--debug`有效 |
193
209
  | cache | string\|bool | No | `true` | 是否缓存记录 | 正常情况打开避免频繁更新,默认位置为临时目录下 `ddns.cache`,也可以指定一个具体路径 |
194
- | log | {"level":string,"file":string} | No | `null` | 日志配置(可选) | 日志配置,日志级别和路径(默认命令行),例如:`{ "level": "DEBUG", "file": "dns.log" }` |
210
+ | log | object | No | `null` | 日志配置(可选) | 日志配置对象,支持`level`、`file`、`format`、`datefmt`参数 |
195
211
 
196
212
  #### index4 和 index6 参数说明
197
213
 
@@ -237,7 +253,9 @@ python run.py -c /path/to/config.json
237
253
  "proxy": "127.0.0.1:1080;DIRECT",
238
254
  "log": {
239
255
  "level": "DEBUG",
240
- "file": "dns.log"
256
+ "file": "dns.log",
257
+ "format": "%(asctime)s %(levelname)s [%(module)s]: %(message)s",
258
+ "datefmt": "%Y-%m-%dT%H:%M:%S"
241
259
  }
242
260
  }
243
261
  ```
@@ -6,13 +6,11 @@ DDNS
6
6
  @modified: rufengsuixing
7
7
  """
8
8
 
9
- # nuitka-project: --product-version=0.0.0
10
-
11
9
  from os import path, environ, name as os_name
12
10
  from io import TextIOWrapper
13
11
  from subprocess import check_output
14
12
  from tempfile import gettempdir
15
- from logging import basicConfig, info, warning, error, debug
13
+ from logging import basicConfig, info, warning, error, debug, DEBUG, NOTSET
16
14
 
17
15
  import sys
18
16
 
@@ -20,8 +18,8 @@ from util import ip
20
18
  from util.cache import Cache
21
19
  from util.config import init_config, get_config
22
20
 
23
- __version__ = "v4.0.0-beta5@2025-06-12T01:13:18+00:00" # CI 时会被Tag替换
24
- __description__ = "automatically update DNS records to dynamic local IP [自动更新DNS记录指向本地IP]"
21
+ __version__ = "v4.0.0-beta7@2025-06-13T14:46:22+00:00" # CI 时会被Tag替换
22
+ __description__ = "automatically update DNS records to my IP [域名自动指向本机IP]"
25
23
  __doc__ = """
26
24
  ddns[%s]
27
25
  (i) homepage or docs [文档主页]: https://ddns.newfuture.cc/
@@ -29,7 +27,7 @@ ddns[%s]
29
27
  Copyright (c) New Future (MIT License)
30
28
  """ % (__version__)
31
29
 
32
- environ["DDNS_VERSION"] = "v4.0.0-beta5"
30
+ environ["DDNS_VERSION"] = "v4.0.0-beta7"
33
31
 
34
32
 
35
33
  def is_false(value):
@@ -127,21 +125,28 @@ def main():
127
125
  更新
128
126
  """
129
127
  init_config(__description__, __doc__, __version__)
130
- # Dynamicly import the dns module as configuration
131
- dns_provider = str(get_config('dns', 'dnspod').lower())
132
- dns = getattr(__import__('dns', fromlist=[dns_provider]), dns_provider)
133
- dns.Config.ID = get_config('id')
134
- dns.Config.TOKEN = get_config('token')
135
- dns.Config.TTL = get_config('ttl')
136
128
 
129
+ log_level = get_config('log.level')
130
+ log_format = get_config('log.format', '%(asctime)s %(levelname)s [%(module)s]: %(message)s')
131
+ # Override log format in debug mode to include filename and line number for detailed debugging
132
+ if (log_level == DEBUG or log_level == NOTSET) and not log_format:
133
+ log_format = '%(asctime)s %(levelname)s [%(filename)s:%(lineno)d]: %(message)s'
137
134
  basicConfig(
138
- level=get_config('log.level'),
139
- format='%(asctime)s [%(levelname)s] %(message)s',
140
- datefmt='%m-%d %H:%M:%S',
135
+ level=log_level,
136
+ format=log_format,
137
+ datefmt=get_config('log.datefmt', '%Y-%m-%dT%H:%M:%S'),
141
138
  filename=get_config('log.file'),
142
139
  )
143
140
 
144
141
  info("DDNS[ %s ] run: %s %s", __version__, os_name, sys.platform)
142
+
143
+ # Dynamically import the dns module as configuration
144
+ dns_provider = str(get_config('dns', 'dnspod').lower())
145
+ dns = getattr(__import__('dns', fromlist=[dns_provider]), dns_provider)
146
+ dns.Config.ID = get_config('id')
147
+ dns.Config.TOKEN = get_config('token')
148
+ dns.Config.TTL = get_config('ttl')
149
+
145
150
  if get_config("config"):
146
151
  info('loaded Config from: %s', path.abspath(get_config('config')))
147
152
 
@@ -169,9 +174,23 @@ def main():
169
174
 
170
175
 
171
176
  if __name__ == '__main__':
172
- encoding = sys.stdout.encoding
173
- if encoding is not None and encoding.lower() != 'utf-8' and hasattr(sys.stdout, 'buffer'):
177
+ encode = sys.stdout.encoding
178
+ if encode is not None and encode.lower() != 'utf-8' and hasattr(sys.stdout, 'buffer'):
174
179
  # 兼容windows 和部分ASCII编码的老旧系统
175
180
  sys.stdout = TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
176
181
  sys.stderr = TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
177
182
  main()
183
+
184
+ # Nuitka Project Configuration
185
+ # nuitka-project: --mode=onefile
186
+ # nuitka-project: --output-filename=ddns
187
+ # nuitka-project: --product-name=DDNS
188
+ # nuitka-project: --product-version=0.0.0
189
+ # nuitka-project: --onefile-tempdir-spec="{TEMP}/{PRODUCT}_{VERSION}"
190
+ # nuitka-project: --no-deployment-flag=self-execution
191
+ # nuitka-project: --company-name="New Future"
192
+ # nuitka-project: --copyright=https://ddns.newfuture.cc
193
+ # nuitka-project: --assume-yes-for-downloads
194
+ # nuitka-project: --python-flag=no_site,no_asserts,no_docstrings,isolated,static_hashes
195
+ # nuitka-project: --nofollow-import-to=tkinter,unittest,pydoc,doctest,distutils,setuptools,lib2to3,test,idlelib,lzma
196
+ # nuitka-project: --noinclude-dlls=liblzma.*
@@ -96,12 +96,22 @@ def init_config(description, doc, version):
96
96
  help='https proxy [设置http 代理,多代理逐个尝试直到成功]')
97
97
  parser.add_argument('--cache', type=str2bool, nargs='?',
98
98
  const=True, help='cache flag [启用缓存,可配配置路径或开关]')
99
+ parser.add_argument('--debug', action='store_true',
100
+ help='debug mode [调试模式,等同log.level=DEBUG]')
99
101
  parser.add_argument('--log.file', metavar='LOG_FILE',
100
102
  help='log file [日志文件,默认标准输出]')
101
103
  parser.add_argument('--log.level', type=log_level,
102
104
  metavar='|'.join(log_levels))
105
+ parser.add_argument('--log.format', metavar='LOG_FORMAT',
106
+ help='log format [日志格式字符串]')
107
+ parser.add_argument('--log.datefmt', metavar='DATE_FORMAT',
108
+ help='date format [日期格式字符串]')
103
109
 
104
110
  __cli_args = parser.parse_args()
111
+ if __cli_args.debug:
112
+ # 如果启用调试模式,则设置日志级别为 DEBUG
113
+ setattr(__cli_args, 'log.level', log_level('DEBUG'))
114
+
105
115
  is_configfile_required = not get_config("token") and not get_config("id")
106
116
  config_file = get_config("config")
107
117
  if not config_file:
@@ -141,6 +151,10 @@ def __load_config(config_path):
141
151
  __config['log.level'] = log_level(__config['log']['level'])
142
152
  if 'file' in __config['log']:
143
153
  __config['log.file'] = __config['log']['file']
154
+ if 'format' in __config['log']:
155
+ __config['log.format'] = __config['log']['format']
156
+ if 'datefmt' in __config['log']:
157
+ __config['log.datefmt'] = __config['log']['datefmt']
144
158
  elif 'log.level' in __config:
145
159
  __config['log.level'] = log_level(__config['log.level'])
146
160
  except Exception as e:
@@ -211,8 +225,7 @@ def generate_config(config_path):
211
225
  'ttl': None,
212
226
  'proxy': None,
213
227
  'log': {
214
- 'level': 'INFO',
215
- 'file': None
228
+ 'level': 'INFO'
216
229
  }
217
230
  }
218
231
  try:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes