Skip to content

Commit 064beb1

Browse files
committed
feat: add diff logic for action & optimize the action
1 parent dad5be4 commit 064beb1

File tree

7 files changed

+2372
-210
lines changed

7 files changed

+2372
-210
lines changed

packages/action/package.json

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,40 @@
11
{
22
"name": "@rsdoctor/action",
33
"version": "0.0.1",
4-
"main": "index.js",
4+
"license": "MIT",
5+
"description": "A GitHub Action for bundle size analysis and reporting",
6+
"main": "./index.js",
57
"scripts": {
68
"build": "rslib build"
79
},
10+
"repository": {
11+
"type": "git",
12+
"url": "https://github.com/web-infra-dev/rsdoctor",
13+
"directory": "packages/actions"
14+
},
15+
"publishConfig": {
16+
"access": "public"
17+
},
818
"files": [
9-
"dist"
19+
"index.js",
20+
"action.yml",
21+
"README.md"
22+
],
23+
"keywords": [
24+
"github-action",
25+
"bundle-size",
26+
"performance",
27+
"analysis"
1028
],
11-
"license": "MIT",
1229
"devDependencies": {
13-
"@rslib/core": "^0.13.2"
30+
"@rslib/core": "^0.13.2",
31+
"@types/node": "^24.5.2"
1432
},
1533
"dependencies": {
1634
"@actions/artifact": "^2.3.2",
1735
"@actions/core": "^1.2.6",
1836
"@actions/github": "^4.0.0",
19-
"size-plugin-core": "0.0.9"
37+
"size-plugin-core": "0.0.9",
38+
"yauzl": "^3.2.0"
2039
}
2140
}

packages/action/src/download.ts

Lines changed: 142 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,167 @@
1-
import { DefaultArtifactClient } from '@actions/artifact';
2-
import { summary } from '@actions/core';
3-
import * as fs from 'fs';
1+
import { setFailed, summary } from '@actions/core';
42
import path from 'path';
3+
import * as fs from 'fs';
54
import { GitHubService } from './github';
5+
import * as yauzl from 'yauzl';
66

77
export async function downloadArtifact(artifactId: number, fileName: string) {
8-
const artifactClient = new DefaultArtifactClient();
8+
console.log(`📥 Downloading artifact ID: ${artifactId}`);
99

10-
// Download the artifact
11-
const downloadResponse = await artifactClient.downloadArtifact(artifactId);
10+
const githubService = new GitHubService();
1211

13-
if (!downloadResponse.downloadPath) {
14-
throw new Error('Artifact download failed: No download path returned.');
15-
}
12+
try {
13+
const downloadResponse = await githubService.downloadArtifact(artifactId);
1614

17-
console.log(
18-
`Successfully downloaded artifact to: ${downloadResponse.downloadPath}`,
19-
);
20-
21-
// Read and log the JSON data from the downloaded artifact
22-
const downloadedFilePath = path.join(downloadResponse.downloadPath, fileName);
23-
const fileContent = await fs.promises.readFile(downloadedFilePath, 'utf-8');
24-
const jsonData = JSON.parse(fileContent);
25-
26-
console.log('--- Downloaded Artifact JSON Data ---');
27-
console.log(jsonData);
28-
console.log('------------------------------------');
29-
30-
// Ensure jsonData is an array for table generation
31-
const dataForTable = Array.isArray(jsonData) ? jsonData : [jsonData];
32-
33-
// Generate and write summary table
34-
if (dataForTable.length > 0) {
35-
const headers = Object.keys(dataForTable[0]);
36-
const headerRow = headers.map((h) => ({ data: h, header: true }));
37-
const bodyRows = dataForTable.map((row) =>
38-
headers.map((header) => {
39-
const cellData = row[header];
40-
// Stringify objects/arrays for proper display in the table
41-
if (typeof cellData === 'object' && cellData !== null) {
42-
return JSON.stringify(cellData);
43-
}
44-
return String(cellData ?? '');
45-
}),
46-
);
15+
const tempDir = path.join(process.cwd(), 'temp-artifact');
16+
await fs.promises.mkdir(tempDir, { recursive: true });
4717

48-
await summary
49-
.addHeading('Artifact Content')
50-
.addTable([headerRow, ...bodyRows])
51-
.write();
52-
console.log('Successfully wrote artifact content to job summary.');
53-
} else {
54-
console.log('JSON data is empty, skipping table generation.');
55-
}
18+
const zipPath = path.join(tempDir, 'artifact.zip');
19+
const buffer = Buffer.from(downloadResponse);
20+
await fs.promises.writeFile(zipPath, buffer);
21+
22+
console.log(`✅ Downloaded artifact zip to: ${zipPath}`);
23+
await new Promise<void>((resolve, reject) => {
24+
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
25+
if (err) return reject(err);
26+
27+
zipfile.readEntry();
28+
zipfile.on('entry', (entry) => {
29+
if (/\/$/.test(entry.fileName)) {
30+
zipfile.readEntry();
31+
} else {
32+
zipfile.openReadStream(entry, (err, readStream) => {
33+
if (err) return reject(err);
34+
35+
const outputPath = path.join(tempDir, entry.fileName);
36+
const outputDir = path.dirname(outputPath);
37+
38+
fs.promises.mkdir(outputDir, { recursive: true }).then(() => {
39+
const writeStream = fs.createWriteStream(outputPath);
40+
readStream.pipe(writeStream);
41+
writeStream.on('close', () => zipfile.readEntry());
42+
});
43+
});
44+
}
45+
});
46+
47+
zipfile.on('end', () => resolve());
48+
zipfile.on('error', reject);
49+
});
50+
});
51+
52+
console.log(`✅ Extracted artifact to: ${tempDir}`);
53+
54+
const extractedFiles = await fs.promises.readdir(tempDir, {
55+
recursive: true,
56+
});
57+
console.log(`📁 Extracted files: ${extractedFiles.join(', ')}`);
58+
59+
let targetFilePath: string | null = null;
60+
for (const file of extractedFiles) {
61+
if (file === fileName || file.endsWith(fileName)) {
62+
targetFilePath = path.join(tempDir, file);
63+
break;
64+
}
65+
}
66+
67+
if (!targetFilePath) {
68+
throw new Error(
69+
`Target file ${fileName} not found in extracted artifact`,
70+
);
71+
}
72+
73+
console.log(`📄 Found target file: ${targetFilePath}`);
74+
75+
const fileContent = await fs.promises.readFile(targetFilePath, 'utf-8');
76+
const jsonData = JSON.parse(fileContent);
5677

57-
return {
58-
downloadPath: downloadResponse.downloadPath,
59-
jsonData: jsonData,
60-
};
78+
console.log('--- Downloaded Artifact JSON Data ---');
79+
console.log(jsonData);
80+
console.log('------------------------------------');
81+
82+
await fs.promises.unlink(zipPath);
83+
84+
return {
85+
downloadPath: tempDir,
86+
jsonData: jsonData,
87+
};
88+
} catch (error) {
89+
console.error(
90+
`❌ Failed to download and extract artifact: ${error.message}`,
91+
);
92+
throw error;
93+
}
6194
}
6295

6396
export async function downloadArtifactByCommitHash(
6497
commitHash: string,
6598
fileName: string,
6699
) {
100+
console.log(`🔍 Looking for artifact with commit hash: ${commitHash}`);
101+
67102
const githubService = new GitHubService();
68103

69-
// Find artifact by commit hash pattern
104+
try {
105+
await githubService.verifyTokenPermissions();
106+
} catch (permissionError) {
107+
console.warn(
108+
`⚠️ Token permission check failed: ${permissionError.message}`,
109+
);
110+
console.log(`🔄 Continuing with artifact search...`);
111+
}
112+
113+
console.log(`📋 Searching for artifacts matching commit hash: ${commitHash}`);
70114
const artifact = await githubService.findArtifactByNamePattern(commitHash);
71115

72116
if (!artifact) {
117+
console.log(`❌ No artifact found for commit hash: ${commitHash}`);
118+
console.log(`💡 This might mean:`);
119+
console.log(` - The target branch hasn't been built yet`);
120+
console.log(` - The artifact name pattern doesn't match`);
121+
console.log(
122+
` - The artifact has expired (GitHub artifacts expire after 90 days)`,
123+
);
73124
throw new Error(`No artifact found for commit hash: ${commitHash}`);
74125
}
75126

76-
console.log(`Found artifact: ${artifact.name} (ID: ${artifact.id})`);
127+
console.log(`✅ Found artifact: ${artifact.name} (ID: ${artifact.id})`);
128+
129+
try {
130+
const artifacts = await githubService.listArtifacts();
131+
const artifactDetails = artifacts.artifacts.find(
132+
(a) => a.id === artifact.id,
133+
);
134+
if (artifactDetails) {
135+
console.log(`📊 Artifact details:`);
136+
console.log(` - Created: ${artifactDetails.created_at}`);
137+
console.log(
138+
` - Expired: ${artifactDetails.expired_at || 'Not expired'}`,
139+
);
140+
console.log(` - Size: ${artifactDetails.size_in_bytes} bytes`);
141+
142+
if (artifactDetails.expired_at) {
143+
console.log(
144+
`⚠️ Warning: This artifact has expired and may not be downloadable`,
145+
);
146+
}
147+
}
148+
} catch (detailError) {
149+
console.warn(`⚠️ Could not get artifact details: ${detailError.message}`);
150+
}
151+
152+
console.log(`📥 Downloading artifact...`);
77153

78-
// Download using the artifact ID
79-
return await downloadArtifact(artifact.id, fileName);
154+
try {
155+
return await downloadArtifact(artifact.id, fileName);
156+
} catch (downloadError) {
157+
console.error(`❌ Download failed with error: ${downloadError.message}`);
158+
console.error(`💡 This usually means:`);
159+
console.error(
160+
` - Token lacks 'actions:read' permission for downloading artifacts`,
161+
);
162+
console.error(` - Artifact is from a different workflow run`);
163+
console.error(` - Artifact download URL is expired or invalid`);
164+
console.error(` - Network or GitHub API issues`);
165+
throw downloadError;
166+
}
80167
}

0 commit comments

Comments
 (0)