Skip to content

Commit aa21836

Browse files
authored
fix: optimize artifact names (#49)
1 parent 1d4ab3b commit aa21836

5 files changed

Lines changed: 128 additions & 14 deletions

File tree

dist/index.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96153,10 +96153,14 @@ var __webpack_exports__ = {};
9615396153
var external_fs_ = __webpack_require__("fs");
9615496154
var external_child_process_ = __webpack_require__("child_process");
9615596155
var external_crypto_ = __webpack_require__("crypto");
96156+
const ARTIFACT_NAME_PREFIX = 'rsdoctor';
9615696157
function hashPath(pathParts, fileNameWithoutExt) {
9615796158
const pathString = `${pathParts.join('-')}-${fileNameWithoutExt}`;
9615896159
return (0, external_crypto_.createHash)('sha256').update(pathString).digest('hex').substring(0, 8);
9615996160
}
96161+
function createArtifactName(pathHash, commitHash) {
96162+
return `${ARTIFACT_NAME_PREFIX}-${pathHash}-${commitHash}`;
96163+
}
9616096164
async function uploadArtifact(filePath, commitHash) {
9616196165
const artifactClient = new lib_artifact.DefaultArtifactClient();
9616296166
const hash = commitHash || (0, external_child_process_.execSync)('git rev-parse --short=10 HEAD', {
@@ -96169,7 +96173,7 @@ var __webpack_exports__ = {};
9616996173
const pathParts = relativePath.split(external_path_default().sep);
9617096174
const fileNameWithoutExt = external_path_default().parse(fileName).name;
9617196175
const pathHash = hashPath(pathParts, fileNameWithoutExt);
96172-
const artifactName = `${pathHash}-${hash}`;
96176+
const artifactName = createArtifactName(pathHash, hash);
9617396177
console.log(`Uploading artifact: ${artifactName}`);
9617496178
console.log(`From file: ${targetFilePath}`);
9617596179
const uploadResponse = await artifactClient.uploadArtifact(artifactName, [
@@ -96637,8 +96641,11 @@ var __webpack_exports__ = {};
9663796641
const fileNameWithoutExt = external_path_default().parse(fileName).name;
9663896642
const fileExt = external_path_default().parse(fileName).ext;
9663996643
const pathHash = hashPath(pathParts, fileNameWithoutExt);
96640-
const expectedArtifactName = `${pathHash}-${commitHash}`;
96641-
const legacyArtifactName = `${pathHash}-${commitHash}${fileExt}`;
96644+
const expectedArtifactName = createArtifactName(pathHash, commitHash);
96645+
const legacyArtifactNames = [
96646+
`${pathHash}-${commitHash}`,
96647+
`${pathHash}-${commitHash}${fileExt}`
96648+
];
9664296649
console.log(`📋 Searching for artifact with path hash and commit hash: ${expectedArtifactName}`);
9664396650
console.log(` Path hash: ${pathHash}`);
9664496651
console.log(` File path: ${relativePath}`);
@@ -96653,7 +96660,7 @@ var __webpack_exports__ = {};
9665396660
console.log(` Status: ${workflowRun.status}, Conclusion: ${workflowRun.conclusion}`);
9665496661
try {
9665596662
const runArtifacts = await githubService.listArtifactsForWorkflowRun(workflowRun.id);
96656-
const foundArtifact = runArtifacts.artifacts?.find((a)=>a.name === expectedArtifactName || a.name === legacyArtifactName);
96663+
const foundArtifact = runArtifacts.artifacts?.find((a)=>a.name === expectedArtifactName || legacyArtifactNames.includes(a.name));
9665796664
if (foundArtifact) {
9665896665
artifact = foundArtifact;
9665996666
artifacts = runArtifacts;
@@ -96679,7 +96686,7 @@ var __webpack_exports__ = {};
9667996686
}
9668096687
if (!artifact) {
9668196688
artifacts = await githubService.listArtifacts();
96682-
artifact = artifacts.artifacts.find((a)=>a.name === expectedArtifactName || a.name === legacyArtifactName);
96689+
artifact = artifacts.artifacts.find((a)=>a.name === expectedArtifactName || legacyArtifactNames.includes(a.name));
9668396690
}
9668496691
if (!artifact) {
9668596692
console.log(`❌ No artifact found matching: ${expectedArtifactName}`);

src/__tests__/download.test.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import * as path from 'path';
2+
import { afterEach, beforeEach, describe, expect, it } from '@rstest/core';
3+
import { downloadArtifactByCommitHash } from '../download';
4+
import { createArtifactName, hashPath } from '../upload';
5+
import { mockConsole, restoreConsole } from './mock-console';
6+
const nock = require('nock');
7+
8+
describe('Download Module', () => {
9+
const commitHash = 'abc1234567';
10+
const filePath = '/tmp/test/rsdoctor-data.json';
11+
const fileName = 'rsdoctor-data.json';
12+
const workflowRunId = 123;
13+
14+
beforeEach(() => {
15+
mockConsole();
16+
nock.cleanAll();
17+
});
18+
19+
afterEach(() => {
20+
nock.cleanAll();
21+
restoreConsole();
22+
});
23+
24+
function getPathHash() {
25+
const relativePath = path.relative(process.cwd(), filePath);
26+
const pathParts = relativePath.split(path.sep);
27+
return hashPath(pathParts, 'rsdoctor-data');
28+
}
29+
30+
function mockArtifactSearch(artifactName: string, artifactId: number) {
31+
nock('https://api.github.com')
32+
.get('/repos/web-infra-dev/rsdoctor-action/actions/runs')
33+
.query({ head_sha: commitHash, status: 'completed', per_page: 30 })
34+
.reply(200, {
35+
workflow_runs: [
36+
{
37+
id: workflowRunId,
38+
name: 'CI',
39+
status: 'completed',
40+
conclusion: 'success',
41+
},
42+
],
43+
});
44+
45+
nock('https://api.github.com')
46+
.get(`/repos/web-infra-dev/rsdoctor-action/actions/runs/${workflowRunId}/artifacts`)
47+
.reply(200, {
48+
artifacts: [
49+
{
50+
id: artifactId,
51+
name: artifactName,
52+
created_at: '2026-05-08T00:00:00Z',
53+
size_in_bytes: 100,
54+
},
55+
],
56+
});
57+
58+
return nock('https://api.github.com')
59+
.get(`/repos/web-infra-dev/rsdoctor-action/actions/artifacts/${artifactId}/zip`)
60+
.reply(200, Buffer.from('not a zip'));
61+
}
62+
63+
it('should find artifacts that use the rsdoctor-prefixed name', async () => {
64+
const pathHash = getPathHash();
65+
const artifactName = createArtifactName(pathHash, commitHash);
66+
const downloadScope = mockArtifactSearch(artifactName, 101);
67+
68+
await expect(downloadArtifactByCommitHash(commitHash, fileName, filePath)).rejects.toThrow();
69+
70+
expect(downloadScope.isDone()).toBe(true);
71+
});
72+
73+
it('should find artifacts that use the legacy unprefixed name', async () => {
74+
const pathHash = getPathHash();
75+
const downloadScope = mockArtifactSearch(`${pathHash}-${commitHash}`, 202);
76+
77+
await expect(downloadArtifactByCommitHash(commitHash, fileName, filePath)).rejects.toThrow();
78+
79+
expect(downloadScope.isDone()).toBe(true);
80+
});
81+
82+
it('should find artifacts that use the legacy unprefixed name with extension', async () => {
83+
const pathHash = getPathHash();
84+
const downloadScope = mockArtifactSearch(`${pathHash}-${commitHash}.json`, 303);
85+
86+
await expect(downloadArtifactByCommitHash(commitHash, fileName, filePath)).rejects.toThrow();
87+
88+
expect(downloadScope.isDone()).toBe(true);
89+
});
90+
});

src/__tests__/upload.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,13 @@ describe('Upload Module', () => {
4949
.toThrow('Target file not found');
5050
});
5151

52-
it('should generate correct artifact name', async () => {
52+
it('should prefix artifact name with rsdoctor for easier cleanup', async () => {
5353
const result = await uploadArtifact(mockFilePath, mockCommitHash);
54+
const relativePath = path.relative(process.cwd(), mockFilePath);
55+
const pathParts = relativePath.split(path.sep);
56+
const pathHash = hashPath(pathParts, 'rsdoctor-data');
57+
58+
expect(console.log).toHaveBeenCalledWith(`Uploading artifact: rsdoctor-${pathHash}-${mockCommitHash}`);
5459
expect(result).toBeDefined();
5560
expect(result.id).toBe(1);
5661
});
@@ -89,4 +94,3 @@ describe('Upload Module', () => {
8994
});
9095
});
9196
});
92-

src/download.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import path from 'path';
22
import * as fs from 'fs';
33
import { GitHubService } from './github';
44
import * as yauzl from 'yauzl';
5-
import { hashPath } from './upload';
5+
import { createArtifactName, hashPath } from './upload';
66

77
export async function downloadArtifact(artifactId: number, fileName: string) {
88
console.log(`📥 Downloading artifact ID: ${artifactId}`);
@@ -106,9 +106,12 @@ export async function downloadArtifactByCommitHash(
106106
const fileNameWithoutExt = path.parse(fileName).name;
107107
const fileExt = path.parse(fileName).ext;
108108
const pathHash = hashPath(pathParts, fileNameWithoutExt);
109-
// New format (no extension); legacy format includes the file extension
110-
const expectedArtifactName = `${pathHash}-${commitHash}`;
111-
const legacyArtifactName = `${pathHash}-${commitHash}${fileExt}`;
109+
const expectedArtifactName = createArtifactName(pathHash, commitHash);
110+
// Legacy formats were used before artifact names were prefixed for cleanup.
111+
const legacyArtifactNames = [
112+
`${pathHash}-${commitHash}`,
113+
`${pathHash}-${commitHash}${fileExt}`,
114+
];
112115

113116
console.log(`📋 Searching for artifact with path hash and commit hash: ${expectedArtifactName}`);
114117
console.log(` Path hash: ${pathHash}`);
@@ -131,7 +134,9 @@ export async function downloadArtifactByCommitHash(
131134

132135
try {
133136
const runArtifacts = await githubService.listArtifactsForWorkflowRun(workflowRun.id);
134-
const foundArtifact = runArtifacts.artifacts?.find((a: any) => a.name === expectedArtifactName || a.name === legacyArtifactName);
137+
const foundArtifact = runArtifacts.artifacts?.find(
138+
(a: any) => a.name === expectedArtifactName || legacyArtifactNames.includes(a.name),
139+
);
135140

136141
if (foundArtifact) {
137142
artifact = foundArtifact;
@@ -160,7 +165,9 @@ export async function downloadArtifactByCommitHash(
160165
// Fallback: if not found in any workflow run, search all repository artifacts
161166
if (!artifact) {
162167
artifacts = await githubService.listArtifacts();
163-
artifact = artifacts.artifacts.find((a: any) => a.name === expectedArtifactName || a.name === legacyArtifactName);
168+
artifact = artifacts.artifacts.find(
169+
(a: any) => a.name === expectedArtifactName || legacyArtifactNames.includes(a.name),
170+
);
164171
}
165172

166173
if (!artifact) {

src/upload.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,17 @@ import * as fs from 'fs';
44
import { execSync } from 'child_process';
55
import { createHash } from 'crypto';
66

7+
export const ARTIFACT_NAME_PREFIX = 'rsdoctor';
8+
79
export function hashPath(pathParts: string[], fileNameWithoutExt: string): string {
810
const pathString = `${pathParts.join('-')}-${fileNameWithoutExt}`;
911
return createHash('sha256').update(pathString).digest('hex').substring(0, 8);
1012
}
1113

14+
export function createArtifactName(pathHash: string, commitHash: string): string {
15+
return `${ARTIFACT_NAME_PREFIX}-${pathHash}-${commitHash}`;
16+
}
17+
1218
export async function uploadArtifact(filePath: string, commitHash?: string) {
1319
const artifactClient = new DefaultArtifactClient();
1420

@@ -26,7 +32,7 @@ export async function uploadArtifact(filePath: string, commitHash?: string) {
2632
const fileNameWithoutExt = path.parse(fileName).name;
2733

2834
const pathHash = hashPath(pathParts, fileNameWithoutExt);
29-
const artifactName = `${pathHash}-${hash}`;
35+
const artifactName = createArtifactName(pathHash, hash);
3036

3137
console.log(`Uploading artifact: ${artifactName}`);
3238
console.log(`From file: ${targetFilePath}`);

0 commit comments

Comments
 (0)