#!/usr/bin/env node const uuid = require('uuid'); const fs = require('fs'); const jwt = require('jsonwebtoken'); const request = require('request-promise-native'); class AMOClient { constructor(issuer, secret) { this.issuer = issuer; this.secret = secret; } async getProfile() { let token = this._getJwtToken(); let response = await request({ url: 'https://addons.mozilla.org/api/v4/accounts/profile/', method: 'GET', headers: { Authorization: 'JWT ' + token, } }); return response; }; async uploadVersion(guid, version, xpiPath) { let token = this._getJwtToken(); const formData = { upload: fs.createReadStream(xpiPath), }; let url = `https://addons.mozilla.org/api/v4/addons/${encodeURIComponent(guid)}/versions/${encodeURIComponent(version)}/` let response = await request({ url, formData, method: 'PUT', headers: { Authorization: 'JWT ' + token, } }); return response; } _getJwtToken() { // See https://addons-server.readthedocs.io/en/latest/topics/api/auth.html const issuedAt = Math.floor(Date.now() / 1000); const payload = { iss: this.issuer, jti: uuid.v4(), iat: issuedAt, exp: issuedAt + 60, }; return jwt.sign(payload, this.secret, { algorithm: 'HS256' }); } } const run = async() => { let args = process.argv; if (args.length != 5) { console.error(`USAGE: ${args[1]} GUID VERSION XPI_PATH`) console.error('') console.error('Environment variables') console.error(' JWT_ISSUER: JWT issuer generated by AMO') console.error(' JWT_SECRET: JWT secret token generated by AMO') process.exit(2); } let guid = args[2]; let version = args[3]; let xpiPath = args[4] let jwtIssuer = process.env['JWT_ISSUER']; let jwtSecret = process.env['JWT_SECRET']; if (typeof jwtIssuer === 'undefined' || jwtIssuer.length === 0) { console.error('JWT_ISSUER not set'); process.exit(2); } if (typeof jwtSecret === 'undefined' || jwtSecret.length === 0) { console.error('JWT_SECRET not set'); process.exit(2); } let amo = new AMOClient(jwtIssuer, jwtSecret); console.log(`Uploading ${xpiPath} to ${guid}/${version}`); let response = await amo.uploadVersion(guid, version, xpiPath); console.log(response); }; run().catch((err) => { console.error(err.error || err.message); process.exit(1); });