Skip to content

Commit 2fe83d5

Browse files
committed
feat: update upload docs script
1 parent 6478436 commit 2fe83d5

2 files changed

Lines changed: 116 additions & 50 deletions

File tree

.github/workflows/deploy-docs.yml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,43 @@ jobs:
5050
env:
5151
DOC_LANG: fr-FR
5252
run: yarn build
53-
- name: Upload docs to OSS and update CDN
53+
- name: Install ossutil
54+
if: ${{ github.ref_name == 'main' }}
55+
run: |
56+
curl -o /tmp/ossutil.zip https://gosspublic.alicdn.com/ossutil/v2/2.2.2/ossutil-2.2.2-linux-amd64.zip
57+
unzip -o /tmp/ossutil.zip -d /tmp/ossutil
58+
sudo mv /tmp/ossutil/ossutil-2.2.2-linux-amd64/ossutil /usr/local/bin/ossutil
59+
sudo chmod 755 /usr/local/bin/ossutil
60+
- name: Upload docs to OSS via ossutil
61+
if: ${{ github.ref_name == 'main' }}
62+
env:
63+
OSS_ACCESS_KEY_ID: ${{ secrets.DOCS_ALI_OSS_ACCESS_KEY_ID }}
64+
OSS_ACCESS_KEY_SECRET: ${{ secrets.DOCS_ALI_OSS_ACCESS_KEY_SECRET }}
65+
run: |
66+
TIMESTAMP=$(date +%Y%m%d%H%M%S)
67+
echo "DOCS_TIMESTAMP=$TIMESTAMP" >> $GITHUB_ENV
68+
REGION=${{ secrets.DOCS_ALI_OSS_REGION }}
69+
BUCKET=${{ secrets.DOCS_ALI_OSS_BUCKET }}
70+
71+
# Upload en-US to timestamp root (default language)
72+
ossutil cp -r ./dist/en-US/ oss://$BUCKET/$TIMESTAMP/ -f --region $REGION
73+
74+
# Upload other language directories
75+
for lang_dir in ./dist/*/; do
76+
lang=$(basename "$lang_dir")
77+
if [ "$lang" != "en-US" ]; then
78+
ossutil cp -r "$lang_dir" "oss://$BUCKET/$TIMESTAMP/$lang/" -f --region $REGION
79+
fi
80+
done
81+
- name: Update CDN and cleanup
5482
if: ${{ github.ref_name == 'main' }}
5583
env:
5684
DOCS_ALI_OSS_ACCESS_KEY_ID: ${{ secrets.DOCS_ALI_OSS_ACCESS_KEY_ID }}
5785
DOCS_ALI_OSS_ACCESS_KEY_SECRET: ${{ secrets.DOCS_ALI_OSS_ACCESS_KEY_SECRET }}
5886
DOCS_ALI_OSS_BUCKET: ${{ secrets.DOCS_ALI_OSS_BUCKET }}
5987
DOCS_ALI_OSS_REGION: ${{ secrets.DOCS_ALI_OSS_REGION }}
6088
DOCS_ALI_CDN_DOMAIN: ${{ secrets.DOCS_ALI_CDN_DOMAIN }}
61-
run: node scripts/upload-docs.js --dir ./dist
89+
run: node scripts/upload-docs.js --skip-upload --timestamp ${{ env.DOCS_TIMESTAMP }}
6290
- name: Set tags
6391
if: ${{ github.ref_name != 'main' }}
6492
id: set-tags

scripts/upload-docs.js

Lines changed: 86 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,33 @@ function normalizeDomain(domain) {
4747
return domain.replace(/^https?:\/\//, '').replace(/\/+$/, '');
4848
}
4949

50+
const UPLOAD_CONCURRENCY = 20;
51+
5052
/**
51-
* Recursively upload a directory to OSS
53+
* Collect all files recursively from a directory
54+
* @returns {Array<{filePath: string, ossKey: string}>}
55+
*/
56+
function collectFiles(localDir, ossPrefix = '') {
57+
const result = [];
58+
if (!fs.existsSync(localDir)) return result;
59+
60+
const entries = fs.readdirSync(localDir);
61+
for (const entry of entries) {
62+
const filePath = path.resolve(localDir, entry);
63+
const fileStats = fs.statSync(filePath);
64+
const key = ossPrefix ? `${ossPrefix}/${entry}` : entry;
65+
66+
if (fileStats.isDirectory()) {
67+
result.push(...collectFiles(filePath, key));
68+
} else {
69+
result.push({ filePath, ossKey: key });
70+
}
71+
}
72+
return result;
73+
}
74+
75+
/**
76+
* Upload a directory to OSS with concurrent uploads
5277
*/
5378
async function uploadDirectoryToOSS(client, localDir, ossPrefix = '') {
5479
if (!fs.existsSync(localDir)) {
@@ -61,26 +86,26 @@ async function uploadDirectoryToOSS(client, localDir, ossPrefix = '') {
6186
throw new Error(`${localDir} is not a directory`);
6287
}
6388

64-
const files = fs.readdirSync(localDir);
89+
const files = collectFiles(localDir, ossPrefix);
6590
let uploadedCount = 0;
6691

67-
for (const file of files) {
68-
const filePath = path.resolve(localDir, file);
69-
const fileStats = fs.statSync(filePath);
70-
71-
if (fileStats.isDirectory()) {
72-
const subOssPrefix = ossPrefix ? `${ossPrefix}/${file}` : file;
73-
const subCount = await uploadDirectoryToOSS(client, filePath, subOssPrefix);
74-
uploadedCount += subCount;
75-
} else {
76-
const ossKey = ossPrefix ? `${ossPrefix}/${file}` : file;
77-
try {
78-
await client.put(ossKey, filePath);
79-
uploadedCount++;
80-
} catch (error) {
81-
console.error(`[error] Failed to upload ${ossKey}: ${error.message}`);
82-
throw error;
83-
}
92+
// Upload in batches with concurrency
93+
for (let i = 0; i < files.length; i += UPLOAD_CONCURRENCY) {
94+
const batch = files.slice(i, i + UPLOAD_CONCURRENCY);
95+
const results = await Promise.all(
96+
batch.map(async ({ filePath, ossKey }) => {
97+
try {
98+
await client.put(ossKey, filePath);
99+
return true;
100+
} catch (error) {
101+
console.error(`[error] Failed to upload ${ossKey}: ${error.message}`);
102+
throw error;
103+
}
104+
}),
105+
);
106+
uploadedCount += results.length;
107+
if (files.length > UPLOAD_CONCURRENCY) {
108+
console.log(`[info] Uploaded ${uploadedCount}/${files.length} files...`);
84109
}
85110
}
86111

@@ -254,6 +279,8 @@ function parseArgs() {
254279
} else if (args[i] === '--timestamp' && args[i + 1]) {
255280
options.timestamp = args[i + 1];
256281
i++;
282+
} else if (args[i] === '--skip-upload') {
283+
options.skipUpload = true;
257284
}
258285
}
259286
return options;
@@ -269,22 +296,23 @@ async function main() {
269296
process.exit(1);
270297
}
271298

272-
// 2. Validate --dir
273-
if (!options.dir) {
299+
// 2. Validate --dir (not required when --skip-upload)
300+
if (!options.skipUpload && !options.dir) {
274301
console.error('[error] Missing required option: --dir <dir>');
275302
console.error('Usage: node scripts/upload-docs.js --dir ./dist');
303+
console.error(' node scripts/upload-docs.js --skip-upload --timestamp <ts>');
276304
process.exit(1);
277305
}
278306

279-
const dir = path.resolve(process.cwd(), options.dir);
280-
if (!fs.existsSync(dir)) {
281-
console.error(`[error] Directory does not exist: ${dir}`);
307+
// 3. Validate --timestamp is required when --skip-upload
308+
if (options.skipUpload && !options.timestamp) {
309+
console.error('[error] --timestamp is required when using --skip-upload');
282310
process.exit(1);
283311
}
284312

285313
const domain = normalizeDomain(process.env.DOCS_ALI_CDN_DOMAIN);
286314

287-
// 3. Create OSS client
315+
// 4. Create OSS client
288316
const Client = require('ali-oss');
289317
const ossClient = new Client({
290318
accessKeyId: process.env.DOCS_ALI_OSS_ACCESS_KEY_ID,
@@ -293,35 +321,45 @@ async function main() {
293321
region: process.env.DOCS_ALI_OSS_REGION,
294322
});
295323

296-
// 4. Generate or use provided timestamp
324+
// 5. Generate or use provided timestamp
297325
const timestamp = options.timestamp || generateTimestamp();
298326

299-
// 5. Upload to OSS
300-
console.log(`[info] Uploading docs from ${dir} to OSS under ${timestamp}/...`);
301-
try {
302-
let uploadedCount = 0;
303-
304-
// Upload en-US to the root of the timestamp directory (default language)
305-
const enUSDir = path.resolve(dir, 'en-US');
306-
if (fs.existsSync(enUSDir)) {
307-
console.log('[info] Uploading en-US as root (default language)...');
308-
uploadedCount += await uploadDirectoryToOSS(ossClient, enUSDir, timestamp);
327+
// 6. Upload to OSS (skip if --skip-upload)
328+
if (!options.skipUpload) {
329+
const dir = path.resolve(process.cwd(), options.dir);
330+
if (!fs.existsSync(dir)) {
331+
console.error(`[error] Directory does not exist: ${dir}`);
332+
process.exit(1);
309333
}
310334

311-
// Upload other language directories (skip en-US since it's already at root)
312-
const langDirs = fs.readdirSync(dir);
313-
for (const lang of langDirs) {
314-
if (lang === 'en-US') continue;
315-
const langDir = path.resolve(dir, lang);
316-
if (fs.statSync(langDir).isDirectory()) {
317-
uploadedCount += await uploadDirectoryToOSS(ossClient, langDir, `${timestamp}/${lang}`);
335+
console.log(`[info] Uploading docs from ${dir} to OSS under ${timestamp}/...`);
336+
try {
337+
let uploadedCount = 0;
338+
339+
// Upload en-US to the root of the timestamp directory (default language)
340+
const enUSDir = path.resolve(dir, 'en-US');
341+
if (fs.existsSync(enUSDir)) {
342+
console.log('[info] Uploading en-US as root (default language)...');
343+
uploadedCount += await uploadDirectoryToOSS(ossClient, enUSDir, timestamp);
318344
}
319-
}
320345

321-
console.log(`[info] Successfully uploaded ${uploadedCount} files to OSS under ${timestamp}/`);
322-
} catch (error) {
323-
console.error(`[error] Upload failed: ${error.message}`);
324-
process.exit(1);
346+
// Upload other language directories (skip en-US since it's already at root)
347+
const langDirs = fs.readdirSync(dir);
348+
for (const lang of langDirs) {
349+
if (lang === 'en-US') continue;
350+
const langDir = path.resolve(dir, lang);
351+
if (fs.statSync(langDir).isDirectory()) {
352+
uploadedCount += await uploadDirectoryToOSS(ossClient, langDir, `${timestamp}/${lang}`);
353+
}
354+
}
355+
356+
console.log(`[info] Successfully uploaded ${uploadedCount} files to OSS under ${timestamp}/`);
357+
} catch (error) {
358+
console.error(`[error] Upload failed: ${error.message}`);
359+
process.exit(1);
360+
}
361+
} else {
362+
console.log(`[info] Skipping upload (--skip-upload), using timestamp: ${timestamp}`);
325363
}
326364

327365
// 6. Update CDN origin rewrite rule

0 commit comments

Comments
 (0)