Skip to content

Commit c4951e5

Browse files
authored
Merge pull request #117 from mdbenito/fix/117-duplicate-attachments
Fixes for S3 attachments
2 parents 5740b8c + 7fac840 commit c4951e5

6 files changed

Lines changed: 67 additions & 43 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ Go to [Settings / Access Tokens](https://gitlab.com/profile/personal_access_toke
5656

5757
Leave it null for the first run of the script. Then the script will show you which projects there are. Can be either string or number.
5858

59+
#### gitlab.sessionCookie
60+
61+
GitLab's API [does not allow downloading of attachments](https://gitlab.com/gitlab-org/gitlab/-/issues/24155) and only images can be downloaded using HTTP. To work around this limitation and enable binary attachments to be migrated one can use the session cookie set in the browser after logging in to the gitlab instance. The cookie is named `_gitlab_session`.
62+
5963
### github
6064

6165
#### github.baseUrl

sample_settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export default {
55
// url: 'https://gitlab.mycompany.com',
66
token: '{{gitlab private token}}',
77
projectId: null,
8+
sessionCookie: null,
89
},
910
github: {
1011
// baseUrl: 'https://gitlab.mycompany.com:123/etc',

src/githubHelper.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -825,9 +825,7 @@ export default class GithubHelper {
825825
}
826826
);
827827

828-
if (settings.s3) {
829-
strWithMigLine = await utils.migrateAttachments(strWithMigLine, this.repoId, settings.s3, this.gitlabHelper);
830-
}
828+
strWithMigLine = await utils.migrateAttachments(strWithMigLine, this.repoId, settings.s3, this.gitlabHelper);
831829

832830
return strWithMigLine;
833831
}

src/gitlabHelper.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default class GitlabHelper {
1111
gitlabUrl?: string;
1212
gitlabToken: string;
1313
gitlabProjectId: number;
14+
sessionCookie: string;
1415

1516
host: string;
1617
projectPath?: string;
@@ -24,6 +25,7 @@ export default class GitlabHelper {
2425
this.gitlabToken = gitlabSettings.token;
2526
this.gitlabProjectId = gitlabSettings.projectId;
2627
this.host = gitlabSettings.url ? gitlabSettings.url : 'http://gitlab.com';
28+
this.sessionCookie = gitlabSettings.sessionCookie;
2729
}
2830

2931
/**
@@ -92,7 +94,13 @@ export default class GitlabHelper {
9294
try {
9395
const host = this.host.endsWith('/') ? this.host : this.host + '/';
9496
const attachmentUrl = host + this.projectPath + relurl;
95-
const data = (await axios.get(attachmentUrl, {responseType: 'arraybuffer'})).data;
97+
const data = (await axios.get(attachmentUrl, {
98+
responseType: 'arraybuffer',
99+
headers: {
100+
// HACK: work around GitLab's API lack of GET for attachments
101+
// See https://gitlab.com/gitlab-org/gitlab/-/issues/24155
102+
Cookie: `_gitlab_session=${this.sessionCookie}`
103+
}})).data;
96104
return Buffer.from(data, 'binary')
97105
} catch (err) {
98106
console.error(`Could not download attachment #${relurl}.`);

src/settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export interface GitlabSettings {
4242
url?: string;
4343
token: string;
4444
projectId: number;
45+
sessionCookie: string;
4546
}
4647

4748
export interface S3Settings {

src/utils.ts

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const generateUserProjectRegex = () => {
3434

3535
// Creates new attachments and replaces old links
3636
export const migrateAttachments = async (body: string, githubRepoId: number | undefined, s3: S3Settings, gitlabHelper: GitlabHelper) => {
37-
const regexp = /!\[([^\]]+)\]\((\/uploads[^)]+)\)/g;
37+
const regexp = /(!?)\[([^\]]+)\]\((\/uploads[^)]+)\)/g;
3838

3939
// Maps link offset to a new name in S3
4040
const offsetToAttachment: {
@@ -45,45 +45,57 @@ export const migrateAttachments = async (body: string, githubRepoId: number | un
4545
const matches = body.matchAll(regexp);
4646

4747
for (const match of matches) {
48-
const name = match[1];
49-
const url = match[2];
50-
51-
const basename = path.basename(url);
52-
const extension = path.extname(url);
53-
const mimeType = mime.lookup(basename);
54-
const attachmentBuffer = await gitlabHelper.getAttachment(url);
55-
56-
// Generate new random file name for S3 bucket
57-
const id = crypto.randomBytes(16).toString('hex');
58-
const newFileName = id + extension;
59-
const relativePath = githubRepoId ? `${githubRepoId}/${newFileName}` : newFileName;
60-
61-
// Doesn't seem like it is easy to upload an issue to github, so upload to S3
62-
//https://stackoverflow.com/questions/41581151/how-to-upload-an-image-to-use-in-issue-comments-via-github-api
63-
64-
const s3url = `https://${s3.bucket}.s3.amazonaws.com/${relativePath}`;
65-
66-
const s3bucket = new S3();
67-
s3bucket.createBucket(() => {
68-
const params: S3.PutObjectRequest = {
69-
Key: relativePath,
70-
Body: attachmentBuffer,
71-
ContentType: mimeType === false ? null : mimeType,
72-
Bucket: s3.bucket,
73-
};
74-
75-
s3bucket.upload(params, function (err, data) {
76-
console.log(`\tUploaded ${basename} to ${s3url}`);
77-
if (err) {
78-
console.log('ERROR MSG: ', err);
79-
}
48+
const prefix = match[1] || '';
49+
const name = match[2];
50+
const url = match[3];
51+
52+
if (s3 && s3.bucket) {
53+
const basename = path.basename(url);
54+
const mimeType = mime.lookup(basename);
55+
const attachmentBuffer = await gitlabHelper.getAttachment(url);
56+
if (!attachmentBuffer) {
57+
continue;
58+
}
59+
60+
// // Generate file name for S3 bucket from URL
61+
const hash = crypto.createHash('sha256');
62+
hash.update(url);
63+
const newFileName = hash.digest('hex') + '/' + basename
64+
const relativePath = githubRepoId ? `${githubRepoId}/${newFileName}` : newFileName;
65+
// Doesn't seem like it is easy to upload an issue to github, so upload to S3
66+
//https://stackoverflow.com/questions/41581151/how-to-upload-an-image-to-use-in-issue-comments-via-github-api
67+
68+
const s3url = `https://${s3.bucket}.s3.amazonaws.com/${relativePath}`;
69+
70+
const s3bucket = new S3();
71+
s3bucket.createBucket(() => {
72+
const params: S3.PutObjectRequest = {
73+
Key: relativePath,
74+
Body: attachmentBuffer,
75+
ContentType: mimeType === false ? null : mimeType,
76+
Bucket: s3.bucket,
77+
};
78+
79+
s3bucket.upload(params, function (err, data) {
80+
console.log(`\tUploading ${basename} to ${s3url}... `);
81+
if (err) {
82+
console.log('ERROR: ', err);
83+
} else {
84+
console.log(`\t...Done uploading`);
85+
}
86+
});
8087
});
81-
});
82-
83-
// Add the new URL to the map
84-
offsetToAttachment[match.index] = `![${name}](${s3url})`;
85-
88+
89+
// Add the new URL to the map
90+
offsetToAttachment[match.index] = `${prefix}[${name}](${s3url})`;
91+
} else {
92+
// Not using S3: default to old URL, adding absolute path
93+
const host = gitlabHelper.host.endsWith('/')
94+
? gitlabHelper.host : gitlabHelper.host + '/';
95+
const attachmentUrl = host + gitlabHelper.projectPath + url;
96+
offsetToAttachment[match.index] = `${prefix}[${name}](${attachmentUrl})`;
97+
}
8698
}
8799

88-
return body.replace(regexp, ({},{},{},offset,{}) => offsetToAttachment[offset]);
100+
return body.replace(regexp, ({},{},{},{},offset,{}) => offsetToAttachment[offset]);
89101
};

0 commit comments

Comments
 (0)