Skip to content

Commit d12ca4f

Browse files
authored
Sync providers and update dynamic index.html
1 parent 317fe2b commit d12ca4f

1 file changed

Lines changed: 104 additions & 47 deletions

File tree

.github/workflows/sync-tf-mirror.yml

Lines changed: 104 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,25 @@ jobs:
2020
with:
2121
python-version: '3.11'
2222

23-
- name: Force Sync Multi-Arch Providers
23+
- name: Force Sync & Gen Index
24+
shell: python
2425
run: |
25-
# 创建镜像根目录
26-
mkdir -p public/tf-mirror
27-
28-
# 执行 Python 脚本
29-
python3 -c "
3026
import json
3127
import urllib.request
3228
import urllib.error
3329
import shutil
3430
import os
3531
import sys
32+
import datetime
3633
from pathlib import Path
3734
from concurrent.futures import ThreadPoolExecutor, as_completed
3835
3936
# ================= 配置区域 =================
40-
# 常用官方小工具
4137
PROVIDERS = [
4238
'random', 'null', 'time', 'local',
4339
'tls', 'http', 'archive', 'external'
4440
]
4541
46-
# 强制同步的平台列表 (Windows, Linux, macOS)
4742
TARGET_PLATFORMS = [
4843
('linux', 'amd64'), ('linux', 'arm64'),
4944
('windows', 'amd64'),
@@ -52,9 +47,14 @@ jobs:
5247
5348
MIRROR_ROOT = Path('public/tf-mirror')
5449
REGISTRY_API = 'https://registry.terraform.io/v1/providers/hashicorp'
50+
# 修改为您实际的 GitHub Pages 地址
51+
MIRROR_URL = 'https://redc.wgpsec.org/tf-mirror/'
5552
MAX_WORKERS = 4
5653
# ===========================================
5754
55+
# 确保目录存在
56+
MIRROR_ROOT.mkdir(parents=True, exist_ok=True)
57+
5858
class MirrorSync:
5959
def __init__(self, base_dir: Path):
6060
self.base_dir = base_dir
@@ -67,7 +67,6 @@ jobs:
6767
def download_file(self, url, dest_path: Path):
6868
if dest_path.exists() and dest_path.stat().st_size > 0:
6969
return f'⏩ Skip: {dest_path.name}'
70-
7170
try:
7271
dest_path.parent.mkdir(parents=True, exist_ok=True)
7372
req = urllib.request.Request(url, headers={'User-Agent': 'TF-Mirror-Bot'})
@@ -81,87 +80,145 @@ jobs:
8180
def process_provider(self, name):
8281
print(f'🔍 Analyzing hashicorp/{name}...')
8382
try:
84-
# 1. 获取最新版本
8583
meta = self.fetch_json(f'{REGISTRY_API}/{name}')
8684
version = meta['version']
8785
88-
# 准备目录结构
89-
# 格式: registry.terraform.io/hashicorp/{name}/{version}/{os}_{arch}/
9086
provider_dir = self.base_dir / 'registry.terraform.io' / 'hashicorp' / name
9187
92-
# 2. 准备下载任务
9388
tasks = {}
9489
platform_meta = []
9590
9691
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
97-
# A. 下载各平台 ZIP 包
9892
for os_name, arch in TARGET_PLATFORMS:
9993
try:
10094
dl_api = f'{REGISTRY_API}/{name}/{version}/download/{os_name}/{arch}'
10195
dl_info = self.fetch_json(dl_api)
102-
10396
filename = dl_info['filename']
10497
file_url = dl_info['download_url']
10598
106-
# 本地存放路径
10799
save_dir = provider_dir / version / f'{os_name}_{arch}'
108100
dest = save_dir / filename
109101
110102
tasks[executor.submit(self.download_file, file_url, dest)] = f'{os_name}/{arch}'
111-
112-
# 记录到索引元数据中
113103
platform_meta.append({'os': os_name, 'arch': arch})
114-
115-
except urllib.error.HTTPError as e:
116-
print(f' ⚠️ {name} {version} not found for {os_name}/{arch}')
104+
except urllib.error.HTTPError:
105+
# 某些版本可能不支持特定架构,忽略即可
106+
pass
117107
118-
# B. 下载 SHA256SUMS 和 签名 (重要! Terraform 校验需要)
119-
# 通常在任意一个下载链接的同级目录
108+
# 下载签名文件
120109
if 'file_url' in locals():
121110
base_url_sums = file_url.rsplit('/', 1)[0]
122111
sums_name = f'terraform-provider-{name}_{version}_SHA256SUMS'
123-
124-
# 存放到 version 根目录
125112
version_root = provider_dir / version
126113
tasks[executor.submit(self.download_file, f'{base_url_sums}/{sums_name}', version_root / sums_name)] = 'SHA256SUMS'
127114
tasks[executor.submit(self.download_file, f'{base_url_sums}/{sums_name}.sig', version_root / f'{sums_name}.sig')] = 'Signature'
128115
129-
# 等待所有下载完成
130116
for future in as_completed(tasks):
131117
print(f' {future.result()}')
132118
133-
# 3. 生成 index.json (关键! Network Mirror 协议)
134-
# 路径: registry.terraform.io/hashicorp/{name}/index.json
135-
index_data = {
136-
'versions': {
137-
version: {
138-
'protocols': ['5.0'],
139-
'platforms': platform_meta
140-
}
141-
}
142-
}
119+
# 生成 index.json
120+
index_data = {'versions': {version: {'protocols': ['5.0'], 'platforms': platform_meta}}}
143121
with open(provider_dir / 'index.json', 'w') as f:
144122
json.dump(index_data, f, indent=2)
145123
146-
return f'✅ Finished: {name} v{version}'
124+
return {'name': name, 'version': version, 'status': 'success'}
147125
148126
except Exception as e:
149-
return f'🚫 Error: {name} - {e}'
127+
print(f'🚫 Error: {name} - {e}')
128+
return {'name': name, 'version': 'Error', 'status': 'failed'}
150129
151-
# --- 主程序 ---
130+
# --- Main Execution ---
152131
syncer = MirrorSync(MIRROR_ROOT)
132+
results = []
153133
154-
# 并发处理多个 Provider
155134
with ThreadPoolExecutor(max_workers=3) as main_executor:
156135
futures = {main_executor.submit(syncer.process_provider, p): p for p in PROVIDERS}
157136
for f in as_completed(futures):
158-
print(f.result())
159-
"
137+
res = f.result()
138+
results.append(res)
139+
print(f"✅ Finished processing {res['name']}")
160140
161-
- name: Verify Windows Files
162-
run: |
163-
echo "📂 Checking for Windows binaries:"
164-
find public/tf-mirror -name "*windows_amd64*" | head -n 5 || echo "No windows files found"
141+
# --- Generate index.html (Dynamic Config) ---
142+
print('📝 Generating index.html...')
143+
timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')
144+
145+
sorted_providers = sorted(PROVIDERS)
146+
config_list = [f'"registry.terraform.io/hashicorp/{p}"' for p in sorted_providers]
147+
config_str = ",\n ".join(config_list)
148+
149+
html_content = f"""
150+
<!DOCTYPE html>
151+
<html lang="en">
152+
<head>
153+
<meta charset="UTF-8">
154+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
155+
<title>Terraform Provider Mirror</title>
156+
<style>
157+
body {{ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 2rem; color: #333; line-height: 1.6; }}
158+
h1 {{ border-bottom: 2px solid #eee; padding-bottom: 0.5rem; }}
159+
.status {{ background: #f6f8fa; padding: 1rem; border-radius: 6px; margin-bottom: 2rem; border: 1px solid #e1e4e8; }}
160+
code {{ background: #eee; padding: 0.2rem 0.4rem; border-radius: 3px; font-family: monospace; font-size: 0.9em; }}
161+
pre {{ background: #2d333b; color: #adbac7; padding: 1.5rem; border-radius: 6px; overflow-x: auto; font-family: monospace; }}
162+
.grid {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; margin-top: 1rem; }}
163+
.card {{ border: 1px solid #e1e4e8; border-radius: 6px; padding: 1rem; transition: transform 0.2s; }}
164+
.card:hover {{ transform: translateY(-2px); box-shadow: 0 4px 6px rgba(0,0,0,0.1); }}
165+
.name {{ font-weight: bold; color: #0366d6; margin-bottom: 0.5rem; }}
166+
.ver {{ font-size: 0.85em; color: #586069; background: #f1f8ff; display: inline-block; padding: 2px 8px; border-radius: 12px; }}
167+
footer {{ margin-top: 3rem; text-align: center; color: #666; font-size: 0.85rem; border-top: 1px solid #eee; padding-top: 1rem; }}
168+
a {{ color: #0366d6; text-decoration: none; }}
169+
a:hover {{ text-decoration: underline; }}
170+
</style>
171+
</head>
172+
<body>
173+
<h1>Terraform Provider Mirror</h1>
174+
<div class="status">
175+
<p><strong>Last Updated:</strong> {timestamp}</p>
176+
<p><strong>Status:</strong> ✅ Ready</p>
177+
</div>
178+
179+
<h2>Usage Configuration</h2>
180+
<p>Add the following to your <code>~/.terraformrc</code> or <code>terraform.rc</code>:</p>
181+
<pre>
182+
provider_installation {{
183+
network_mirror {{
184+
url = "{MIRROR_URL}"
185+
# Automatically generated based on synced providers
186+
include = [
187+
{config_str}
188+
]
189+
}}
190+
direct {{
191+
# Exclude mirrors to avoid conflicts
192+
exclude = [
193+
{config_str}
194+
]
195+
}}
196+
}}</pre>
197+
198+
<h2>Synced Providers ({len(results)})</h2>
199+
<div class="grid">
200+
"""
201+
202+
for r in sorted(results, key=lambda x: x['name']):
203+
if r['status'] == 'success':
204+
html_content += f"""
205+
<div class="card">
206+
<div class="name">hashicorp/{r['name']}</div>
207+
<div class="ver">v{r['version']}</div>
208+
</div>"""
209+
210+
html_content += """
211+
</div>
212+
<footer>
213+
wgpsec terraform mirror | <a href="https://github.com/wgpsec/redc-template">Source Repository</a>
214+
</footer>
215+
</body>
216+
</html>
217+
"""
218+
219+
with open(MIRROR_ROOT / 'index.html', 'w', encoding='utf-8') as f:
220+
f.write(html_content)
221+
print('✅ index.html created successfully.')
165222
166223
- name: Deploy to gh-pages
167224
uses: peaceiris/actions-gh-pages@v3
@@ -170,4 +227,4 @@ jobs:
170227
publish_dir: ./public
171228
publish_branch: gh-pages
172229
keep_files: true
173-
commit_message: "Mirror: Force sync Windows/Linux/Mac tools"
230+
commit_message: "Mirror: Sync providers & update dynamic index.html"

0 commit comments

Comments
 (0)