Skip to content

Commit 734b1d1

Browse files
authored
implement source file upload (#2859)
* implement source file upload * version-source to upload-source-code; reword helptext; versionSource in submit-addon.js to submissionSource; versionPatchData to patchData.version * refactor so call to doFormDataPatch isn't duplicated * validate submissionSource and raise if not valid
1 parent 4c0ba34 commit 734b1d1

File tree

5 files changed

+234
-11
lines changed

5 files changed

+234
-11
lines changed

src/cmd/sign.js

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default function sign(
4040
verbose,
4141
channel,
4242
amoMetadata,
43+
uploadSourceCode,
4344
webextVersion,
4445
},
4546
{
@@ -152,6 +153,7 @@ export default function sign(
152153
validationCheckTimeout: timeout,
153154
approvalCheckTimeout:
154155
approvalTimeout !== undefined ? approvalTimeout : timeout,
156+
submissionSource: uploadSourceCode,
155157
});
156158
} else {
157159
const {

src/program.js

+8
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,14 @@ Example: $0 --help run.
601601
'Only used with `use-submission-api`',
602602
type: 'string',
603603
},
604+
'upload-source-code': {
605+
describe:
606+
'Path to an archive file containing human readable source code of this submission, ' +
607+
'if the code in --source-dir has been processed to make it unreadable. ' +
608+
'See https://extensionworkshop.com/documentation/publish/source-code-submission/ for ' +
609+
'details. Only used with `use-submission-api`',
610+
type: 'string',
611+
},
604612
},
605613
)
606614
.command('run', 'Run the extension', commands.run, {

src/util/submit-addon.js

+54-8
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,32 @@ export default class Client {
185185
return this.fetchJson(url, 'PUT', JSON.stringify(jsonData));
186186
}
187187

188-
async doAfterSubmit(addonId, newVersionId, editUrl) {
188+
async doFormDataPatch(data, addonId, versionId) {
189+
const patchUrl = new URL(
190+
`addon/${addonId}/versions/${versionId}/`,
191+
this.apiUrl,
192+
);
193+
try {
194+
const formData = new FormData();
195+
for (const field in data) {
196+
formData.set(field, data[field]);
197+
}
198+
199+
const response = await this.fetch(patchUrl, 'PATCH', formData);
200+
if (!response.ok) {
201+
throw new Error(`response status was ${response.status}`);
202+
}
203+
} catch (error) {
204+
log.info(`Upload of ${Object.keys(data)} failed: ${error}.`);
205+
throw new Error(`Uploading ${Object.keys(data)} failed`);
206+
}
207+
}
208+
209+
async doAfterSubmit(addonId, newVersionId, editUrl, patchData) {
210+
if (patchData && patchData.version) {
211+
log.info(`Submitting ${Object.keys(patchData.version)} to version`);
212+
await this.doFormDataPatch(patchData.version, addonId, newVersionId);
213+
}
189214
if (this.approvalCheckTimeout > 0) {
190215
const fileUrl = new URL(
191216
await this.waitForApproval(addonId, newVersionId),
@@ -237,7 +262,7 @@ export default class Client {
237262
}
238263

239264
async fetch(url, method = 'GET', body) {
240-
log.info(`Fetching URL: ${url.href}`);
265+
log.info(`${method}ing URL: ${url.href}`);
241266
let headers = {
242267
Authorization: await this.apiAuth.getAuthHeader(),
243268
Accept: 'application/json',
@@ -350,6 +375,7 @@ export default class Client {
350375
uploadUuid,
351376
savedIdPath,
352377
metaDataJson,
378+
patchData,
353379
saveIdToFileFunc = saveIdToFile,
354380
) {
355381
const {
@@ -362,15 +388,15 @@ export default class Client {
362388
log.info('You must add the following to your manifest:');
363389
log.info(`"browser_specific_settings": {"gecko": {"id": "${addonId}"}}`);
364390

365-
return this.doAfterSubmit(addonId, newVersionId, editUrl);
391+
return this.doAfterSubmit(addonId, newVersionId, editUrl, patchData);
366392
}
367393

368-
async putVersion(uploadUuid, addonId, metaDataJson) {
394+
async putVersion(uploadUuid, addonId, metaDataJson, patchData) {
369395
const {
370396
version: { id: newVersionId, edit_url: editUrl },
371397
} = await this.doNewAddonOrVersionSubmit(addonId, uploadUuid, metaDataJson);
372398

373-
return this.doAfterSubmit(addonId, newVersionId, editUrl);
399+
return this.doAfterSubmit(addonId, newVersionId, editUrl, patchData);
374400
}
375401
}
376402

@@ -388,6 +414,7 @@ export async function signAddon({
388414
savedIdPath,
389415
savedUploadUuidPath,
390416
metaDataJson = {},
417+
submissionSource,
391418
userAgentString,
392419
SubmitClient = Client,
393420
ApiAuthClass = JwtApiAuth,
@@ -396,7 +423,7 @@ export async function signAddon({
396423
const stats = await fsPromises.stat(xpiPath);
397424

398425
if (!stats.isFile()) {
399-
throw new Error(`not a file: ${xpiPath}`);
426+
throw new Error('not a file');
400427
}
401428
} catch (statError) {
402429
throw new Error(`error with ${xpiPath}: ${statError}`);
@@ -423,14 +450,33 @@ export async function signAddon({
423450
channel,
424451
savedUploadUuidPath,
425452
);
453+
const patchData = {};
454+
// if we have a source file we need to upload we patch after the create
455+
if (submissionSource) {
456+
try {
457+
const stats2 = await fsPromises.stat(submissionSource);
458+
459+
if (!stats2.isFile()) {
460+
throw new Error('not a file');
461+
}
462+
} catch (statError) {
463+
throw new Error(`error with ${submissionSource}: ${statError}`);
464+
}
465+
patchData.version = { source: client.fileFromSync(submissionSource) };
466+
}
426467

427468
// We specifically need to know if `id` has not been passed as a parameter because
428469
// it's the indication that a new add-on should be created, rather than a new version.
429470
if (id === undefined) {
430-
return client.postNewAddon(uploadUuid, savedIdPath, metaDataJson);
471+
return client.postNewAddon(
472+
uploadUuid,
473+
savedIdPath,
474+
metaDataJson,
475+
patchData,
476+
);
431477
}
432478

433-
return client.putVersion(uploadUuid, id, metaDataJson);
479+
return client.putVersion(uploadUuid, id, metaDataJson, patchData);
434480
}
435481

436482
export async function saveIdToFile(filePath, id) {

tests/unit/test-cmd/test.sign.js

+17
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,23 @@ describe('sign', () => {
384384
});
385385
}));
386386

387+
it('passes the uploadSourceCode parameter to submissionAPI signer as submissionSource', () =>
388+
withTempDir((tmpDir) => {
389+
const stubs = getStubs();
390+
const uploadSourceCode = 'path/to/source.zip';
391+
return sign(tmpDir, stubs, {
392+
extraArgs: {
393+
uploadSourceCode,
394+
useSubmissionApi: true,
395+
channel: 'unlisted',
396+
},
397+
}).then(() => {
398+
sinon.assert.called(stubs.signingOptions.submitAddon);
399+
sinon.assert.calledWithMatch(stubs.signingOptions.submitAddon, {
400+
submissionSource: uploadSourceCode,
401+
});
402+
});
403+
}));
387404
it('returns a signing result', () =>
388405
withTempDir((tmpDir) => {
389406
const stubs = getStubs();

0 commit comments

Comments
 (0)