diff options
-rw-r--r-- | .circleci/config.yml | 38 | ||||
-rw-r--r-- | package-lock.json | 172 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rwxr-xr-x | script/deploy | 91 |
4 files changed, 278 insertions, 25 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 46cf0a6..1294557 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -47,7 +47,7 @@ commands: - restore_cache: key: dependency-cache-{{ checksum "package-lock.json" }} - run: - name: Install npm wee + name: Install dependencies command: npm install - save_cache: key: dependency-cache-{{ checksum "package-lock.json" }} @@ -77,6 +77,19 @@ jobs: - run: npm run build - run: npm run test:e2e + deploy: + executor: + name: default + steps: + - checkout + - setup_npm + - run: npm run package + - run: + name: Deploy to AMO + command: | + version=$(jq -r '.version' manifest.json) + ./script/deploy vim-vixen@i-beam.org "$version" "vim-vixen-${version}.zip" + workflows: version: 2 build_and_test: @@ -86,3 +99,26 @@ workflows: filters: branches: ignore: /^greenkeeper\/.*/ + deploy: + jobs: + - build: + filters: + tags: + only: /^.*/ + branches: + ignore: /.*/ + - e2e: + filters: + tags: + only: /^.*/ + branches: + ignore: /.*/ + - deploy: + requires: + - build + - e2e + filters: + tags: + only: /^.*/ + branches: + ignore: /.*/ diff --git a/package-lock.json b/package-lock.json index 6eabf8c..07869a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1104,6 +1104,12 @@ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "dev": true }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "dev": true + }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -2090,6 +2096,15 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3166,7 +3181,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3187,12 +3203,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3207,17 +3225,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3334,7 +3355,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3346,6 +3368,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3360,6 +3383,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3367,12 +3391,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3391,6 +3417,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3478,7 +3505,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3490,6 +3518,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3575,7 +3604,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3611,6 +3641,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3630,6 +3661,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3673,12 +3705,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -4756,6 +4790,32 @@ "graceful-fs": "^4.1.6" } }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dev": true, + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -4796,6 +4856,27 @@ "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", "dev": true }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "karma": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/karma/-/karma-4.2.0.tgz", @@ -5088,7 +5169,7 @@ } }, "lanthan": { - "version": "git+https://github.com/ueokande/lanthan.git#fb7f641846369de5bac7762f19186d23248f0d53", + "version": "git+https://github.com/ueokande/lanthan.git#d7f92eb4c1c6eee3f747b4a76bdf39062b4cb58f", "from": "git+https://github.com/ueokande/lanthan.git#master", "dev": true, "requires": { @@ -5300,6 +5381,48 @@ "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=", + "dev": true + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", + "dev": true + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=", + "dev": true + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, "lodash.unescape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", @@ -7662,14 +7785,6 @@ "dev": true, "requires": { "lodash": "^4.17.11" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } } }, "request-promise-native": { @@ -8988,12 +9103,21 @@ "dev": true }, "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "requires": { - "punycode": "^1.4.1" + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } } }, "trim-newlines": { diff --git a/package.json b/package.json index c99a84c..2306396 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "eslint": "^6.2.2", "eslint-plugin-react": "^7.14.3", "html-webpack-plugin": "^3.2.0", + "jsonwebtoken": "^8.5.1", "jszip": "^3.2.2", "karma": "^4.2.0", "karma-firefox-launcher": "^1.2.0", @@ -55,6 +56,7 @@ "redux": "^4.0.4", "redux-promise": "^0.6.0", "reflect-metadata": "^0.1.13", + "request-promise-native": "^1.0.7", "sass-loader": "^7.3.1", "sinon": "^7.4.1", "sinon-chrome": "^3.0.1", diff --git a/script/deploy b/script/deploy new file mode 100755 index 0000000..3cc27b8 --- /dev/null +++ b/script/deploy @@ -0,0 +1,91 @@ +#!/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); +}); |