Skip to content

Commit 01689ac

Browse files
committed
feat: 将静态资源自动同步至 s3
1 parent bc5ffba commit 01689ac

File tree

2 files changed

+192
-8
lines changed

2 files changed

+192
-8
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
name: Deploy to server and S3
2+
3+
on:
4+
push:
5+
branches:
6+
- 'main'
7+
paths:
8+
- 'docs/**'
9+
- '.github/workflows/cf.yml'
10+
- '.github/workflows/deploy-to-server.yml'
11+
- '.github/workflows/deploy-to-server-and-s3.yml'
12+
- 'package.json'
13+
workflow_dispatch:
14+
schedule:
15+
- cron: '0 0 1 * *'
16+
17+
jobs:
18+
s3-sync:
19+
runs-on: ubuntu-latest
20+
21+
env:
22+
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY }}
23+
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_KEY }}
24+
S3_BUCKET: lee-blog
25+
S3_ENDPOINT: https://s3.cstcloud.cn
26+
URL_EXPIRES: 3000000 # 3000000 秒 ≈ 34.7 天
27+
28+
steps:
29+
- name: Checkout repository
30+
uses: actions/checkout@v4
31+
with:
32+
fetch-depth: 0
33+
34+
- name: Upload static files to S3
35+
run: |
36+
python3 <<'EOF'
37+
import os
38+
import mimetypes
39+
import boto3
40+
41+
bucket = os.environ["S3_BUCKET"]
42+
endpoint = os.environ["S3_ENDPOINT"]
43+
44+
s3 = boto3.client(
45+
"s3",
46+
endpoint_url=endpoint,
47+
aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
48+
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
49+
)
50+
51+
exts = {".jpg", ".jpeg", ".png", ".webp", ".gif", ".svg", ".ico",
52+
".mp4", ".webm", ".ogg"}
53+
54+
for root, _, files in os.walk("."):
55+
for fname in files:
56+
if os.path.splitext(fname)[1].lower() in exts:
57+
fpath = os.path.join(root, fname)
58+
key = os.path.relpath(fpath, ".")
59+
mime, _ = mimetypes.guess_type(fpath)
60+
mime = mime or "application/octet-stream"
61+
with open(fpath, "rb") as f:
62+
s3.put_object(
63+
Bucket=bucket,
64+
Key=key,
65+
Body=f,
66+
ContentType=mime,
67+
ContentDisposition="inline"
68+
)
69+
print(f"Uploaded {fpath} as {key} ({mime})")
70+
EOF
71+
72+
73+
- name: Generate pre-signed URLs and replace references
74+
run: |
75+
python3 - <<'EOF'
76+
import os, re, subprocess
77+
78+
bucket = os.environ['S3_BUCKET']
79+
endpoint = os.environ['S3_ENDPOINT']
80+
expires = int(os.environ['URL_EXPIRES'])
81+
82+
# 遍历静态文件生成预签名 URL
83+
url_map = {}
84+
for root, dirs, files in os.walk('.'):
85+
for file in files:
86+
if file.lower().endswith((
87+
'.jpg', '.jpeg', '.png', '.webp', '.gif', '.svg', '.ico',
88+
'.mp4', '.webm', '.ogg'
89+
)):
90+
local_path = os.path.join(root, file)
91+
key = os.path.relpath(local_path, '.').replace('\\', '/')
92+
result = subprocess.run([
93+
'aws','s3','presign',
94+
f's3://{bucket}/{key}',
95+
'--expires-in', str(expires),
96+
'--endpoint-url', endpoint
97+
], capture_output=True, text=True)
98+
if result.returncode != 0:
99+
raise RuntimeError(f"预签名失败 {key}: {result.stderr.strip()}")
100+
url = result.stdout.strip()
101+
url_map[key] = url
102+
print(f"预签名 URL: {key} -> {url}")
103+
104+
# 只处理 HTML 和 Markdown 文件
105+
pattern_md = re.compile(r'!\[.*?\]\((.*?)\)')
106+
pattern_html = re.compile(r'(src|href)=["\'](.*?)["\']')
107+
108+
replace_exts = ('.html','.md')
109+
for root, dirs, files in os.walk('.'):
110+
for file in files:
111+
if file.endswith(replace_exts):
112+
path = os.path.join(root, file)
113+
with open(path,'r',encoding='utf-8') as f:
114+
content = f.read()
115+
116+
# 替换 Markdown 图片
117+
def md_repl(match):
118+
src = match.group(1).lstrip('./').replace('\\','/')
119+
# 拼接 docs/ 前缀去查字典
120+
key = f'docs/{src}' if not src.startswith('docs/') else src
121+
url = url_map.get(key)
122+
return f'![]({url})' if url else match.group(0)
123+
124+
content = pattern_md.sub(md_repl, content)
125+
126+
# 替换 HTML src/href
127+
def html_repl(match):
128+
attr, src = match.groups()
129+
src_clean = src.lstrip('./').replace('\\','/')
130+
key = f'docs/{src_clean}' if not src_clean.startswith('docs/') else src_clean
131+
url = url_map.get(key)
132+
return f'{attr}="{url}"' if url else match.group(0)
133+
134+
content = pattern_html.sub(html_repl, content)
135+
136+
with open(path,'w',encoding='utf-8') as f:
137+
f.write(content)
138+
EOF
139+
140+
- name: Pack all files including hidden
141+
run: |
142+
tar -czf processed.tar.gz docs tsconfig.json uno.config.ts package.json pnpm-lock.yaml .git
143+
144+
- name: Upload artifact
145+
uses: actions/upload-artifact@v4
146+
with:
147+
name: processed-source
148+
path: processed.tar.gz
149+
150+
deploy:
151+
runs-on: ubuntu-latest
152+
needs: s3-sync
153+
154+
steps:
155+
- name: Download processed sources
156+
uses: actions/download-artifact@v4
157+
with:
158+
name: processed-source
159+
path: .
160+
161+
- name: Extract
162+
run: tar -xzf processed.tar.gz
163+
164+
- name: Install pnpm
165+
uses: pnpm/action-setup@v3
166+
167+
- name: Install dependencies
168+
run: pnpm install
169+
170+
- name: Update theme package
171+
run: pnpm update @project-trans/vitepress-theme-project-trans@prerelease
172+
173+
- name: Build project
174+
run: pnpm run build
175+
176+
- name: Deploy to Server via SSH rsync
177+
uses: easingthemes/ssh-deploy@v5.1.0
178+
with:
179+
ARGS: "-avz --delete --progress"
180+
REMOTE_HOST: ${{ secrets.SSH_HOST }}
181+
REMOTE_USER: ${{ secrets.SSH_USER }}
182+
SSH_PRIVATE_KEY: ${{ secrets.SSH_KEY }}
183+
SOURCE: "docs/.vitepress/dist/"
184+
TARGET: "/var/www/blog/"

.github/workflows/deploy-to-server.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
name: Deploy to server
22

33
on:
4-
push:
5-
branches:
6-
- 'main'
7-
paths:
8-
- 'docs/**'
9-
- '.github/workflows/cf.yml'
10-
- '.github/workflows/deploy-to-server.yml'
11-
- 'package.json'
4+
# push:
5+
# branches:
6+
# - 'main'
7+
# paths:
8+
# - 'docs/**'
9+
# - '.github/workflows/cf.yml'
10+
# - '.github/workflows/deploy-to-server.yml'
11+
# - 'package.json'
1212
workflow_dispatch:
1313

1414
jobs:

0 commit comments

Comments
 (0)