pixelarraylib 1.0.0__py3-none-any.whl
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.
- arraylib/__init__.py +36 -0
- arraylib/__main__.py +126 -0
- arraylib/aliyun/__init__.py +0 -0
- arraylib/aliyun/aliyun_email.py +130 -0
- arraylib/aliyun/billing.py +477 -0
- arraylib/aliyun/content_scanner.py +253 -0
- arraylib/aliyun/domain.py +434 -0
- arraylib/aliyun/eci.py +47 -0
- arraylib/aliyun/ecs.py +68 -0
- arraylib/aliyun/fc.py +142 -0
- arraylib/aliyun/oss.py +649 -0
- arraylib/aliyun/sms.py +59 -0
- arraylib/aliyun/sts.py +124 -0
- arraylib/db_utils/mysql.py +544 -0
- arraylib/db_utils/redis.py +373 -0
- arraylib/decorators/__init__.py +13 -0
- arraylib/decorators/decorators.py +194 -0
- arraylib/gitlab/__init__.py +0 -0
- arraylib/gitlab/code_analyzer.py +344 -0
- arraylib/gitlab/pypi_package_manager.py +61 -0
- arraylib/monitor/__init__.py +0 -0
- arraylib/monitor/feishu.py +132 -0
- arraylib/net/request.py +143 -0
- arraylib/scripts/__init__.py +22 -0
- arraylib/scripts/collect_code_to_txt.py +327 -0
- arraylib/scripts/create_test_case_files.py +100 -0
- arraylib/scripts/nginx_proxy_to_ecs.py +119 -0
- arraylib/scripts/remove_empty_lines.py +120 -0
- arraylib/scripts/summary_code_count.py +430 -0
- arraylib/system/__init__.py +0 -0
- arraylib/system/common.py +390 -0
- pixelarraylib-1.0.0.dist-info/METADATA +141 -0
- pixelarraylib-1.0.0.dist-info/RECORD +37 -0
- pixelarraylib-1.0.0.dist-info/WHEEL +5 -0
- pixelarraylib-1.0.0.dist-info/entry_points.txt +2 -0
- pixelarraylib-1.0.0.dist-info/licenses/LICENSE +21 -0
- pixelarraylib-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ArrayLib 脚本工具包
|
|
3
|
+
包含各种实用的命令行工具和脚本
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__version__ = "1.0.0"
|
|
7
|
+
__author__ = "Lu qi"
|
|
8
|
+
|
|
9
|
+
# 导出主要的脚本函数
|
|
10
|
+
from arraylib.scripts.create_test_case_files import main as create_test_case_files
|
|
11
|
+
from arraylib.scripts.summary_code_count import main as summary_code_count
|
|
12
|
+
from arraylib.scripts.collect_code_to_txt import main as collect_code_to_txt
|
|
13
|
+
from arraylib.scripts.nginx_proxy_to_ecs import main as nginx_proxy_to_ecs
|
|
14
|
+
from arraylib.scripts.remove_empty_lines import main as remove_empty_lines
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"create_test_case_files",
|
|
18
|
+
"summary_code_count",
|
|
19
|
+
"collect_code_to_txt",
|
|
20
|
+
"nginx_proxy_to_ecs",
|
|
21
|
+
"remove_empty_lines",
|
|
22
|
+
]
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Git仓库代码收集工具
|
|
4
|
+
该脚本用于收集当前目录下所有git仓库中提交的代码文件,并写入txt文件
|
|
5
|
+
|
|
6
|
+
使用方法:
|
|
7
|
+
1. 作为命令行工具:
|
|
8
|
+
arraylib collect_code_to_txt --output=all_code.txt
|
|
9
|
+
arraylib collect_code_to_txt --extensions="py,js,vue" --output=frontend_code.txt
|
|
10
|
+
arraylib collect_code_to_txt --since="2024-01-01" --output=recent_code.txt
|
|
11
|
+
|
|
12
|
+
2. 作为Python模块:
|
|
13
|
+
from arraylib.scripts.collect_code_to_txt import collect_git_repos_code
|
|
14
|
+
collect_git_repos_code(output_file="code.txt")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import subprocess
|
|
19
|
+
import argparse
|
|
20
|
+
from datetime import datetime, timedelta
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
import hashlib
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GitCodeCollector:
|
|
26
|
+
def __init__(self, base_dir="."):
|
|
27
|
+
self.base_dir = Path(base_dir).resolve()
|
|
28
|
+
self.collected_files = set() # 用于去重
|
|
29
|
+
self.file_hashes = {} # 用于检测重复内容
|
|
30
|
+
self.stats = {
|
|
31
|
+
"repos_found": 0,
|
|
32
|
+
"files_collected": 0,
|
|
33
|
+
"total_lines": 0,
|
|
34
|
+
"total_size": 0,
|
|
35
|
+
"errors": 0
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
def is_git_repo(self, path):
|
|
39
|
+
"""检查目录是否为git仓库"""
|
|
40
|
+
return (Path(path) / ".git").exists()
|
|
41
|
+
|
|
42
|
+
def find_git_repos(self):
|
|
43
|
+
"""查找当前目录下的所有git仓库"""
|
|
44
|
+
git_repos = []
|
|
45
|
+
|
|
46
|
+
# 检查当前目录是否为git仓库
|
|
47
|
+
if self.is_git_repo(self.base_dir):
|
|
48
|
+
git_repos.append(self.base_dir)
|
|
49
|
+
|
|
50
|
+
# 递归查找子目录中的git仓库
|
|
51
|
+
for item in self.base_dir.iterdir():
|
|
52
|
+
if item.is_dir() and not item.name.startswith('.'):
|
|
53
|
+
if self.is_git_repo(item):
|
|
54
|
+
git_repos.append(item)
|
|
55
|
+
|
|
56
|
+
return git_repos
|
|
57
|
+
|
|
58
|
+
def run_git_command(self, command, cwd):
|
|
59
|
+
"""执行git命令"""
|
|
60
|
+
try:
|
|
61
|
+
result = subprocess.run(
|
|
62
|
+
command,
|
|
63
|
+
shell=True,
|
|
64
|
+
cwd=cwd,
|
|
65
|
+
capture_output=True,
|
|
66
|
+
text=True,
|
|
67
|
+
check=True
|
|
68
|
+
)
|
|
69
|
+
return result.stdout.strip()
|
|
70
|
+
except subprocess.CalledProcessError as e:
|
|
71
|
+
print(f"⚠️ Git命令执行失败: {command}")
|
|
72
|
+
print(f" 错误信息: {e.stderr}")
|
|
73
|
+
return ""
|
|
74
|
+
|
|
75
|
+
def get_committed_files(self, repo_path, since_date=None, until_date=None, extensions=None):
|
|
76
|
+
"""获取git仓库中已提交的文件列表"""
|
|
77
|
+
# 获取所有提交的文件
|
|
78
|
+
command = "git ls-files"
|
|
79
|
+
if since_date:
|
|
80
|
+
command += f' --since="{since_date}"'
|
|
81
|
+
|
|
82
|
+
output = self.run_git_command(command, repo_path)
|
|
83
|
+
if not output:
|
|
84
|
+
return []
|
|
85
|
+
|
|
86
|
+
files = []
|
|
87
|
+
for line in output.split('\n'):
|
|
88
|
+
if line.strip():
|
|
89
|
+
file_path = Path(repo_path) / line.strip()
|
|
90
|
+
if file_path.exists() and file_path.is_file():
|
|
91
|
+
# 检查文件扩展名
|
|
92
|
+
if extensions:
|
|
93
|
+
file_ext = file_path.suffix.lower()
|
|
94
|
+
if file_ext not in extensions:
|
|
95
|
+
continue
|
|
96
|
+
files.append(file_path)
|
|
97
|
+
|
|
98
|
+
return files
|
|
99
|
+
|
|
100
|
+
def get_file_content(self, file_path):
|
|
101
|
+
"""读取文件内容,处理编码问题"""
|
|
102
|
+
try:
|
|
103
|
+
# 首先尝试UTF-8
|
|
104
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
105
|
+
content = f.read()
|
|
106
|
+
return content
|
|
107
|
+
except UnicodeDecodeError:
|
|
108
|
+
try:
|
|
109
|
+
# 尝试其他编码
|
|
110
|
+
with open(file_path, 'r', encoding='latin-1') as f:
|
|
111
|
+
content = f.read()
|
|
112
|
+
return content
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f"⚠️ 无法读取文件 {file_path}: {e}")
|
|
115
|
+
return None
|
|
116
|
+
except Exception as e:
|
|
117
|
+
print(f"⚠️ 读取文件失败 {file_path}: {e}")
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
def calculate_file_hash(self, content):
|
|
121
|
+
"""计算文件内容的哈希值"""
|
|
122
|
+
return hashlib.md5(content.encode('utf-8')).hexdigest()
|
|
123
|
+
|
|
124
|
+
def should_skip_file(self, file_path, content):
|
|
125
|
+
"""判断是否应该跳过文件"""
|
|
126
|
+
# 跳过空文件
|
|
127
|
+
if not content or not content.strip():
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
# 跳过二进制文件(简单检测)
|
|
131
|
+
if '\x00' in content:
|
|
132
|
+
return True
|
|
133
|
+
|
|
134
|
+
# 跳过过大的文件(超过1MB)
|
|
135
|
+
if len(content) > 1024 * 1024:
|
|
136
|
+
print(f"⚠️ 跳过大文件: {file_path} ({len(content)} 字节)")
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
# 检查重复内容
|
|
140
|
+
content_hash = self.calculate_file_hash(content)
|
|
141
|
+
if content_hash in self.file_hashes:
|
|
142
|
+
print(f"⚠️ 跳过重复文件: {file_path} (与 {self.file_hashes[content_hash]} 内容相同)")
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
self.file_hashes[content_hash] = str(file_path)
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
def collect_repo_files(self, repo_path, output_file, extensions=None, since_date=None, until_date=None):
|
|
149
|
+
"""收集单个git仓库的文件"""
|
|
150
|
+
repo_name = repo_path.name
|
|
151
|
+
print(f"📁 正在处理仓库: {repo_name}")
|
|
152
|
+
|
|
153
|
+
files = self.get_committed_files(repo_path, since_date, until_date, extensions)
|
|
154
|
+
if not files:
|
|
155
|
+
print(f" ⚠️ 未找到符合条件的文件")
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
print(f" 📄 找到 {len(files)} 个文件")
|
|
159
|
+
|
|
160
|
+
repo_files_count = 0
|
|
161
|
+
repo_lines_count = 0
|
|
162
|
+
|
|
163
|
+
for file_path in files:
|
|
164
|
+
try:
|
|
165
|
+
content = self.get_file_content(file_path)
|
|
166
|
+
if content is None:
|
|
167
|
+
self.stats["errors"] += 1
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
if self.should_skip_file(file_path, content):
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
# 写入文件内容
|
|
174
|
+
relative_path = file_path.relative_to(self.base_dir)
|
|
175
|
+
output_file.write(f"\n\n{'='*80}\n")
|
|
176
|
+
output_file.write(f"文件: {relative_path}\n")
|
|
177
|
+
output_file.write(f"仓库: {repo_name}\n")
|
|
178
|
+
output_file.write(f"大小: {len(content)} 字节\n")
|
|
179
|
+
output_file.write(f"行数: {len(content.splitlines())}\n")
|
|
180
|
+
output_file.write(f"{'='*80}\n\n")
|
|
181
|
+
output_file.write(content)
|
|
182
|
+
output_file.write("\n")
|
|
183
|
+
|
|
184
|
+
repo_files_count += 1
|
|
185
|
+
repo_lines_count += len(content.splitlines())
|
|
186
|
+
self.stats["files_collected"] += 1
|
|
187
|
+
self.stats["total_lines"] += len(content.splitlines())
|
|
188
|
+
self.stats["total_size"] += len(content)
|
|
189
|
+
|
|
190
|
+
except Exception as e:
|
|
191
|
+
print(f" ⚠️ 处理文件失败 {file_path}: {e}")
|
|
192
|
+
self.stats["errors"] += 1
|
|
193
|
+
|
|
194
|
+
print(f" ✅ 收集了 {repo_files_count} 个文件,共 {repo_lines_count} 行")
|
|
195
|
+
|
|
196
|
+
def collect_all_repos(self, output_file_path, extensions=None, since_date=None, until_date=None):
|
|
197
|
+
"""收集所有git仓库的代码文件"""
|
|
198
|
+
print("🔍 正在搜索Git仓库...")
|
|
199
|
+
git_repos = self.find_git_repos()
|
|
200
|
+
|
|
201
|
+
if not git_repos:
|
|
202
|
+
print("❌ 未找到任何Git仓库")
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
self.stats["repos_found"] = len(git_repos)
|
|
206
|
+
print(f"📦 找到 {len(git_repos)} 个Git仓库")
|
|
207
|
+
|
|
208
|
+
# 显示仓库列表
|
|
209
|
+
for i, repo in enumerate(git_repos, 1):
|
|
210
|
+
print(f" {i}. {repo.name} ({repo})")
|
|
211
|
+
|
|
212
|
+
print(f"\n📝 开始收集代码文件...")
|
|
213
|
+
print(f"📁 输出文件: {output_file_path}")
|
|
214
|
+
if extensions:
|
|
215
|
+
print(f"📄 文件类型: {', '.join(extensions)}")
|
|
216
|
+
if since_date:
|
|
217
|
+
print(f"📅 开始日期: {since_date}")
|
|
218
|
+
if until_date:
|
|
219
|
+
print(f"📅 结束日期: {until_date}")
|
|
220
|
+
|
|
221
|
+
with open(output_file_path, 'w', encoding='utf-8') as output_file:
|
|
222
|
+
# 写入文件头
|
|
223
|
+
output_file.write("Git仓库代码收集报告\n")
|
|
224
|
+
output_file.write("=" * 80 + "\n")
|
|
225
|
+
output_file.write(f"收集时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
226
|
+
output_file.write(f"基础目录: {self.base_dir}\n")
|
|
227
|
+
output_file.write(f"仓库数量: {len(git_repos)}\n")
|
|
228
|
+
if extensions:
|
|
229
|
+
output_file.write(f"文件类型: {', '.join(extensions)}\n")
|
|
230
|
+
if since_date:
|
|
231
|
+
output_file.write(f"开始日期: {since_date}\n")
|
|
232
|
+
if until_date:
|
|
233
|
+
output_file.write(f"结束日期: {until_date}\n")
|
|
234
|
+
output_file.write("=" * 80 + "\n\n")
|
|
235
|
+
|
|
236
|
+
# 收集每个仓库的文件
|
|
237
|
+
for repo_path in git_repos:
|
|
238
|
+
self.collect_repo_files(
|
|
239
|
+
repo_path,
|
|
240
|
+
output_file,
|
|
241
|
+
extensions,
|
|
242
|
+
since_date,
|
|
243
|
+
until_date
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# 打印统计信息
|
|
247
|
+
self.print_stats(output_file_path)
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
def print_stats(self, output_file_path):
|
|
251
|
+
"""打印统计信息"""
|
|
252
|
+
print(f"\n📊 收集完成统计:")
|
|
253
|
+
print(f" 📦 处理的仓库数: {self.stats['repos_found']}")
|
|
254
|
+
print(f" 📄 收集的文件数: {self.stats['files_collected']}")
|
|
255
|
+
print(f" 📝 总代码行数: {self.stats['total_lines']:,}")
|
|
256
|
+
print(f" 💾 总文件大小: {self.stats['total_size']:,} 字节 ({self.stats['total_size']/1024/1024:.2f} MB)")
|
|
257
|
+
print(f" ⚠️ 错误数量: {self.stats['errors']}")
|
|
258
|
+
print(f" 📁 输出文件: {output_file_path}")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def collect_git_repos_code(output_file="collected_code.txt", extensions=None, since_date=None, until_date=None, base_dir="."):
|
|
262
|
+
"""
|
|
263
|
+
收集Git仓库代码的主函数
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
output_file: 输出文件名
|
|
267
|
+
extensions: 文件扩展名列表,如 ['.py', '.js']
|
|
268
|
+
since_date: 开始日期 (YYYY-MM-DD)
|
|
269
|
+
until_date: 结束日期 (YYYY-MM-DD)
|
|
270
|
+
base_dir: 基础目录
|
|
271
|
+
"""
|
|
272
|
+
collector = GitCodeCollector(base_dir)
|
|
273
|
+
|
|
274
|
+
# 处理扩展名格式
|
|
275
|
+
if extensions and isinstance(extensions, str):
|
|
276
|
+
extensions = [ext.strip() for ext in extensions.split(',')]
|
|
277
|
+
|
|
278
|
+
# 确保扩展名以点开头
|
|
279
|
+
if extensions:
|
|
280
|
+
extensions = [ext if ext.startswith('.') else f'.{ext}' for ext in extensions]
|
|
281
|
+
|
|
282
|
+
success = collector.collect_all_repos(output_file, extensions, since_date, until_date)
|
|
283
|
+
return success
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def main():
|
|
287
|
+
parser = argparse.ArgumentParser(
|
|
288
|
+
description="Git仓库代码收集工具",
|
|
289
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
290
|
+
epilog=__doc__
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
parser.add_argument("--output", "-o", default="collected_code.txt",
|
|
294
|
+
help="输出文件名 (默认: collected_code.txt)")
|
|
295
|
+
parser.add_argument("--extensions", "-e",
|
|
296
|
+
help="文件扩展名,用逗号分隔 (如: py,js,vue)")
|
|
297
|
+
parser.add_argument("--since", "-s",
|
|
298
|
+
help="开始日期 (格式: YYYY-MM-DD)")
|
|
299
|
+
parser.add_argument("--until", "-u",
|
|
300
|
+
help="结束日期 (格式: YYYY-MM-DD)")
|
|
301
|
+
parser.add_argument("--base-dir", "-d", default=".",
|
|
302
|
+
help="基础目录 (默认: 当前目录)")
|
|
303
|
+
|
|
304
|
+
args = parser.parse_args()
|
|
305
|
+
|
|
306
|
+
# 处理文件扩展名
|
|
307
|
+
extensions = None
|
|
308
|
+
if args.extensions:
|
|
309
|
+
extensions = [ext.strip() for ext in args.extensions.split(',')]
|
|
310
|
+
|
|
311
|
+
# 执行收集
|
|
312
|
+
success = collect_git_repos_code(
|
|
313
|
+
output_file=args.output,
|
|
314
|
+
extensions=extensions,
|
|
315
|
+
since_date=args.since,
|
|
316
|
+
until_date=args.until,
|
|
317
|
+
base_dir=args.base_dir
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
if success:
|
|
321
|
+
print(f"\n✅ 代码收集完成!文件已保存到: {args.output}")
|
|
322
|
+
else:
|
|
323
|
+
print(f"\n❌ 代码收集失败!")
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
if __name__ == "__main__":
|
|
327
|
+
main()
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
本脚本用于一键创建所有测试用例文件
|
|
3
|
+
使用方法:
|
|
4
|
+
1. 作为命令行工具:
|
|
5
|
+
arraylib create_test_case_files
|
|
6
|
+
|
|
7
|
+
2. 作为Python模块:
|
|
8
|
+
from arraylib.scripts.create_test_case_files import main
|
|
9
|
+
main()
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import fnmatch
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def walk_dir(dir_path):
|
|
17
|
+
for file in os.listdir(dir_path):
|
|
18
|
+
if os.path.isfile(os.path.join(dir_path, file)):
|
|
19
|
+
yield os.path.join(dir_path, file)
|
|
20
|
+
else:
|
|
21
|
+
yield from walk_dir(os.path.join(dir_path, file))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def main():
|
|
25
|
+
# step1: 检查是否存在test_case目录
|
|
26
|
+
if not os.path.exists("test_case"):
|
|
27
|
+
print("test_case目录不存在!创建test_case目录")
|
|
28
|
+
os.makedirs("test_case")
|
|
29
|
+
else:
|
|
30
|
+
print("test_case目录存在!")
|
|
31
|
+
|
|
32
|
+
# step2:找出所有需要创建的测试用例文件
|
|
33
|
+
not_need_dir = [
|
|
34
|
+
"test_case",
|
|
35
|
+
"scripts",
|
|
36
|
+
".venv",
|
|
37
|
+
".git",
|
|
38
|
+
".idea",
|
|
39
|
+
"dist",
|
|
40
|
+
"build",
|
|
41
|
+
"docs",
|
|
42
|
+
"tests",
|
|
43
|
+
"src",
|
|
44
|
+
"tests",
|
|
45
|
+
"__pycache__",
|
|
46
|
+
".pytest_cache",
|
|
47
|
+
]
|
|
48
|
+
not_need_path = []
|
|
49
|
+
not_need_file = [
|
|
50
|
+
"__init__.py",
|
|
51
|
+
"__main__.py",
|
|
52
|
+
"wsgi.py",
|
|
53
|
+
"settings.py",
|
|
54
|
+
"local_settings.py",
|
|
55
|
+
"test.py",
|
|
56
|
+
"temp.py",
|
|
57
|
+
"base.py",
|
|
58
|
+
"main.py",
|
|
59
|
+
"setup.py",
|
|
60
|
+
]
|
|
61
|
+
test_case_files = []
|
|
62
|
+
for file_path in filter(lambda x: x.endswith(".py"), walk_dir(os.getcwd())):
|
|
63
|
+
relative_path = os.path.relpath(file_path, os.getcwd())
|
|
64
|
+
dirs = os.path.dirname(relative_path).split("/")
|
|
65
|
+
# 检查是否匹配通配符路径
|
|
66
|
+
if any(
|
|
67
|
+
fnmatch.fnmatch(os.path.dirname(relative_path), path)
|
|
68
|
+
for path in not_need_path
|
|
69
|
+
):
|
|
70
|
+
continue
|
|
71
|
+
if (
|
|
72
|
+
any(dir in not_need_dir for dir in dirs[::-1])
|
|
73
|
+
or os.path.basename(file_path) in not_need_file
|
|
74
|
+
):
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
test_case_files.append(
|
|
78
|
+
os.path.join(
|
|
79
|
+
"test_case",
|
|
80
|
+
os.path.dirname(relative_path),
|
|
81
|
+
"test_" + os.path.basename(file_path),
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# step3: 检查并创建测试用例文件
|
|
86
|
+
created_test_case_files = []
|
|
87
|
+
for test_case_file_path in test_case_files:
|
|
88
|
+
if not os.path.exists(test_case_file_path):
|
|
89
|
+
os.makedirs(os.path.dirname(test_case_file_path), exist_ok=True)
|
|
90
|
+
open(test_case_file_path, "w").close()
|
|
91
|
+
created_test_case_files.append(test_case_file_path)
|
|
92
|
+
|
|
93
|
+
# step4: 打印创建的测试用例文件
|
|
94
|
+
print(f"已成功创建测试用例文件{len(created_test_case_files)}个,文件路径如下:")
|
|
95
|
+
for test_case_file_path in created_test_case_files:
|
|
96
|
+
print(test_case_file_path)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
main()
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import base64
|
|
3
|
+
from arraylib.system.common import execute_command_through_ssh
|
|
4
|
+
from arraylib.aliyun.domain import DomainUtils
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def nginx_proxy_file_template(
|
|
8
|
+
domain_name: str, port_of_service: str, ssl_cert_path: str, ssl_key_path: str
|
|
9
|
+
) -> str:
|
|
10
|
+
return f"""
|
|
11
|
+
server {{
|
|
12
|
+
listen 80;
|
|
13
|
+
server_name {domain_name}.pixelarrayai.com;
|
|
14
|
+
|
|
15
|
+
# 将所有HTTP请求重定向到HTTPS
|
|
16
|
+
return 301 https://$host$request_uri;
|
|
17
|
+
}}
|
|
18
|
+
|
|
19
|
+
server {{
|
|
20
|
+
listen 443 ssl;
|
|
21
|
+
server_name {domain_name}.pixelarrayai.com;
|
|
22
|
+
|
|
23
|
+
ssl_certificate {ssl_cert_path};
|
|
24
|
+
ssl_certificate_key {ssl_key_path};
|
|
25
|
+
|
|
26
|
+
location / {{
|
|
27
|
+
proxy_pass http://localhost:{port_of_service};
|
|
28
|
+
proxy_http_version 1.1;
|
|
29
|
+
proxy_set_header Host $host;
|
|
30
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
31
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
32
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
33
|
+
}}
|
|
34
|
+
}}
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def add_a_record_to_dns(
|
|
39
|
+
domain_name: str, ecs_ip: str, access_key_id: str, access_key_secret: str
|
|
40
|
+
) -> None:
|
|
41
|
+
domain_utils = DomainUtils(
|
|
42
|
+
access_key_id, access_key_secret, domain_name="pixelarrayai.com"
|
|
43
|
+
)
|
|
44
|
+
domain_utils.add_domain_record(
|
|
45
|
+
rr=domain_name,
|
|
46
|
+
type="A",
|
|
47
|
+
value=ecs_ip,
|
|
48
|
+
)
|
|
49
|
+
print("域名解析记录添加成功")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def deploy(
|
|
53
|
+
ecs_ip: str,
|
|
54
|
+
domain_name: str,
|
|
55
|
+
port_of_service: str,
|
|
56
|
+
ssl_cert_path: str,
|
|
57
|
+
ssl_key_path: str,
|
|
58
|
+
access_key_id: str,
|
|
59
|
+
access_key_secret: str,
|
|
60
|
+
) -> None:
|
|
61
|
+
execute_command_through_ssh(
|
|
62
|
+
ecs_ip,
|
|
63
|
+
f"sudo rm -f /etc/nginx/sites-available/{domain_name} && sudo rm -f /etc/nginx/sites-enabled/{domain_name}",
|
|
64
|
+
)
|
|
65
|
+
print("删除原有配置成功")
|
|
66
|
+
execute_command_through_ssh(
|
|
67
|
+
ecs_ip,
|
|
68
|
+
f"sudo touch /etc/nginx/sites-available/{domain_name}",
|
|
69
|
+
)
|
|
70
|
+
print("文件创建成功")
|
|
71
|
+
nginx_proxy_file_content = nginx_proxy_file_template(
|
|
72
|
+
domain_name, port_of_service, ssl_cert_path, ssl_key_path
|
|
73
|
+
)
|
|
74
|
+
# 使用 base64 编码来避免特殊字符问题
|
|
75
|
+
encoded_content = base64.b64encode(nginx_proxy_file_content.encode('utf-8')).decode('utf-8')
|
|
76
|
+
execute_command_through_ssh(
|
|
77
|
+
ecs_ip,
|
|
78
|
+
f"echo '{encoded_content}' | base64 -d | sudo tee /etc/nginx/sites-available/{domain_name} > /dev/null",
|
|
79
|
+
)
|
|
80
|
+
print("内容写入成功")
|
|
81
|
+
execute_command_through_ssh(
|
|
82
|
+
ecs_ip,
|
|
83
|
+
f"sudo ln -s /etc/nginx/sites-available/{domain_name} /etc/nginx/sites-enabled/{domain_name}",
|
|
84
|
+
)
|
|
85
|
+
print("nginx配置添加成功,准备重启")
|
|
86
|
+
execute_command_through_ssh(
|
|
87
|
+
ecs_ip, f"sudo nginx -t && sudo systemctl restart nginx"
|
|
88
|
+
)
|
|
89
|
+
print("重启成功,请检查配置是否生效")
|
|
90
|
+
add_a_record_to_dns(domain_name, ecs_ip, access_key_id, access_key_secret)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def main():
|
|
94
|
+
parser = argparse.ArgumentParser(
|
|
95
|
+
description="Nginx反向代理配置到ECS",
|
|
96
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
97
|
+
epilog=__doc__,
|
|
98
|
+
)
|
|
99
|
+
parser.add_argument("--ecs_ip", "-e", help="服务IP地址")
|
|
100
|
+
parser.add_argument("--domain_name", "-d", help="需要代理的域名")
|
|
101
|
+
parser.add_argument("--port_of_service", "-p", help="端口或服务")
|
|
102
|
+
parser.add_argument("--access_key_id", "-a", help="阿里云AccessKeyID")
|
|
103
|
+
parser.add_argument("--access_key_secret", "-s", help="阿里云AccessKeySecret")
|
|
104
|
+
|
|
105
|
+
args = parser.parse_args()
|
|
106
|
+
|
|
107
|
+
deploy(
|
|
108
|
+
args.ecs_ip,
|
|
109
|
+
args.domain_name,
|
|
110
|
+
args.port_of_service,
|
|
111
|
+
"/var/pixelarray/ssl_auth/pixelarrayai.com.pem",
|
|
112
|
+
"/var/pixelarray/ssl_auth/pixelarrayai.com.key",
|
|
113
|
+
args.access_key_id,
|
|
114
|
+
args.access_key_secret,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
main()
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
去除Markdown文档中的所有空行
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
arraylib remove_empty_lines <input_file> [output_file]
|
|
7
|
+
|
|
8
|
+
参数:
|
|
9
|
+
input_file 输入的Markdown文件路径
|
|
10
|
+
output_file 输出的文件路径(可选,默认覆盖原文件)
|
|
11
|
+
|
|
12
|
+
示例:
|
|
13
|
+
arraylib remove_empty_lines test.md
|
|
14
|
+
arraylib remove_empty_lines test.md output.md
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
import argparse
|
|
19
|
+
import os
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def remove_empty_lines(input_file, output_file=None):
|
|
24
|
+
"""
|
|
25
|
+
去除文件中的所有空行
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
input_file (str): 输入文件路径
|
|
29
|
+
output_file (str, optional): 输出文件路径,如果为None则覆盖原文件
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
# 检查输入文件是否存在
|
|
33
|
+
if not os.path.exists(input_file):
|
|
34
|
+
print(f"错误:文件 '{input_file}' 不存在")
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
# 读取文件内容
|
|
38
|
+
with open(input_file, 'r', encoding='utf-8') as f:
|
|
39
|
+
lines = f.readlines()
|
|
40
|
+
|
|
41
|
+
# 过滤掉空行(包括只包含空白字符的行)
|
|
42
|
+
filtered_lines = [line for line in lines if line.strip()]
|
|
43
|
+
|
|
44
|
+
# 确定输出文件路径
|
|
45
|
+
if output_file is None:
|
|
46
|
+
output_file = input_file
|
|
47
|
+
|
|
48
|
+
# 写入输出文件
|
|
49
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
50
|
+
f.writelines(filtered_lines)
|
|
51
|
+
|
|
52
|
+
# 统计信息
|
|
53
|
+
original_lines = len(lines)
|
|
54
|
+
filtered_lines_count = len(filtered_lines)
|
|
55
|
+
removed_lines = original_lines - filtered_lines_count
|
|
56
|
+
|
|
57
|
+
print(f"处理完成!")
|
|
58
|
+
print(f"原始行数: {original_lines}")
|
|
59
|
+
print(f"处理后行数: {filtered_lines_count}")
|
|
60
|
+
print(f"移除空行数: {removed_lines}")
|
|
61
|
+
print(f"输出文件: {output_file}")
|
|
62
|
+
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
print(f"错误:处理文件时发生异常: {e}")
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def main():
|
|
71
|
+
"""主函数"""
|
|
72
|
+
parser = argparse.ArgumentParser(
|
|
73
|
+
description="去除Markdown文档中的所有空行",
|
|
74
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
75
|
+
epilog="""
|
|
76
|
+
示例用法:
|
|
77
|
+
arraylib remove_empty_lines test.md # 去除test.md中的空行并覆盖原文件
|
|
78
|
+
arraylib remove_empty_lines test.md output.md # 去除test.md中的空行并保存到output.md
|
|
79
|
+
"""
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
parser.add_argument(
|
|
83
|
+
"input_file",
|
|
84
|
+
help="输入的Markdown文件路径"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"output_file",
|
|
89
|
+
nargs="?",
|
|
90
|
+
help="输出的文件路径(可选,默认覆盖原文件)"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# 如果没有命令行参数,显示帮助信息
|
|
94
|
+
if len(sys.argv) == 1:
|
|
95
|
+
parser.print_help()
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
args = parser.parse_args()
|
|
99
|
+
|
|
100
|
+
# 检查输入文件扩展名
|
|
101
|
+
input_path = Path(args.input_file)
|
|
102
|
+
if not input_path.suffix.lower() in ['.md', '.markdown', '.txt']:
|
|
103
|
+
print(f"警告:文件 '{args.input_file}' 不是标准的Markdown文件")
|
|
104
|
+
response = input("是否继续处理?(y/N): ")
|
|
105
|
+
if response.lower() not in ['y', 'yes']:
|
|
106
|
+
print("操作已取消")
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
# 执行去除空行操作
|
|
110
|
+
success = remove_empty_lines(args.input_file, args.output_file)
|
|
111
|
+
|
|
112
|
+
if success:
|
|
113
|
+
print("✅ 空行去除完成!")
|
|
114
|
+
else:
|
|
115
|
+
print("❌ 空行去除失败!")
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
main()
|