From 92cfc9451ff3bf78486384a07804663d927ce6b0 Mon Sep 17 00:00:00 2001 From: eolme Date: Sun, 16 May 2021 20:42:40 +0400 Subject: [PATCH 01/21] Rewrite --- .eslintrc.json | 21 ++ .gitignore | 3 + bin/vk-miniapps-deploy | 47 +--- index.js | 391 +------------------------- lib/auth.js | 43 +++ lib/confirm.js | 29 ++ lib/deploy.js | 151 ++++++++++ lib/index.js | 106 +++++++ lib/upload.js | 59 ++++ package-lock.json | 618 ----------------------------------------- package.json | 30 +- 11 files changed, 430 insertions(+), 1068 deletions(-) create mode 100644 .eslintrc.json create mode 100644 lib/auth.js create mode 100644 lib/confirm.js create mode 100644 lib/deploy.js create mode 100644 lib/index.js create mode 100644 lib/upload.js delete mode 100644 package-lock.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..7ee819f --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": [ + "@vkontakte" + ], + "env": { + "es6": true, + "node": true + }, + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module", + "requireConfigFile": false + }, + "rules": { + "guard-for-in": "off", + "comma-dangle": [ + "error", + "never" + ] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7de6599..e142b89 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ node_modules/ build/ bundle.zip vk-hosting-config.json +*.log +*.lock +*-lock.json diff --git a/bin/vk-miniapps-deploy b/bin/vk-miniapps-deploy index 22c9569..4bd2d67 100755 --- a/bin/vk-miniapps-deploy +++ b/bin/vk-miniapps-deploy @@ -1,48 +1,3 @@ #!/usr/bin/env node -var chalk = require('chalk'); -const fs = require('fs-extra'); -const configFilePath = './vk-hosting-config.json'; -const prompt = require('prompts'); -async function run() { - if (fs.pathExists(configFilePath).then((res) => { - if (!res) { - console.error(configFilePath + ' is missing'); - return false; - } - })) ; - - const configJSON = require('require-module')(configFilePath); - if (!configJSON) { - console.error(configFilePath + ' is missing'); - return false; - } - - const deploy = require('../index'); - const cfg = configJSON || {}; - - if (!cfg) { - console.error('vk-hosting-config.json is missing'); - return false; - } - - if (cfg.noprompt) { - deploy.run(cfg); - } else { - prompt.message = chalk.cyan("vk-mini-apps-deploy:"); - prompt.delimiter = chalk.green(" $ "); - - const result = await prompt({ - type: 'confirm', - initial: true, - name: 'result', - message: chalk.yellow('Would you like to deploy to VK Mini Apps hosting using these commands?') - }); - - if (result.result) { - const status = await deploy.run(cfg) ? 0 : 1; - process.exit(status); - } - } -} -run().then(r => console.log(r)); +require('../lib/index'); diff --git a/index.js b/index.js index d84faf4..dab97ef 100644 --- a/index.js +++ b/index.js @@ -1,390 +1 @@ -const packageJson = require('./package.json'); -const chalk = require('chalk'); -const prompt = require('prompts'); -const fetch = require('node-fetch'); -const { zip } = require('zip-a-folder'); -const fs = require('fs-extra'); -const FormData = require('form-data'); -const Configstore = require('configstore'); -const glob = require('glob'); -const vault = new Configstore(packageJson.name, {}); - -var configJSON = require('require-module')('./vk-hosting-config.json'); -var cfg = configJSON || {}; -prompt.message = "vk-mini-apps-deploy".grey; -prompt.delimiter = "=>".grey; - -const API_HOST = cfg.api_host || 'https://api.vk.com/method/'; -const OAUTH_HOST = cfg.oauth_host || 'https://oauth.vk.com/'; - -const API_VERSION = '5.131'; -const DEPLOY_APP_ID = 6670517; - -const CLIENT_VERSION = 2; - -const APPLICATION_ENV_DEV = 1; -const APPLICATION_ENV_PRODUCTION = 2; - -const CODE_SUCCESS = 200; -const CODE_DEPLOY = 201; -const CODE_SKIP = 202; -const CODE_PUSH_SENT_VIA_PUSH = 203; -const CODE_PUSH_APPROVED = 204; -const CODE_CONFIRM_SENT_VIA_MESSAGE = 205; - -const TYPE_SUCCESS = 'success'; - -const URL_NAMES = { - DESKTOP_DEV : 'vk_app_desktop_dev_url', - MOBILE_DEV: 'vk_app_dev_url', - MOBILE_WEB_DEV: 'vk_mini_app_mvk_dev_url', - WEB_LEGACY: 'iframe_url', - WEB: 'iframe_secure_url', - MOBILE: 'm_iframe_secure_url', - MOBILE_WEB: 'vk_mini_app_mvk_url', -} - -const PLATFORMS = { - WEB: 'vk.com', - MOBILE: 'iOS & Android', - MOBILE_WEB: 'm.vk.com', -}; - -const URL_NAMES_MAP = { - [URL_NAMES.DESKTOP_DEV]: PLATFORMS.WEB, - [URL_NAMES.MOBILE_DEV]: PLATFORMS.MOBILE, - [URL_NAMES.MOBILE_WEB_DEV]: PLATFORMS.MOBILE_WEB, - [URL_NAMES.WEB_LEGACY]: PLATFORMS.WEB, - [URL_NAMES.WEB]: PLATFORMS.WEB, - [URL_NAMES.MOBILE]: PLATFORMS.MOBILE, - [URL_NAMES.MOBILE_WEB]: PLATFORMS.MOBILE_WEB, -}; - -async function auth() { - const get_auth_code = await fetch(OAUTH_HOST + 'get_auth_code?scope=offline&client_id=' + DEPLOY_APP_ID); - const get_auth_code_res = await get_auth_code.json(); - - if (get_auth_code_res.error !== void 0) { - throw new Error(JSON.stringify(get_auth_code_res.error)); - } - - if (get_auth_code_res.response !== void 0) { - console.log('fail, get_auth_code response ', get_auth_code_res); - return get_auth_code_res.response; - } - - if (get_auth_code_res.auth_code) { - const {auth_code, device_id} = get_auth_code_res; - - const url = OAUTH_HOST + 'code_auth?stage=check&code=' + auth_code; - - let handled = false; - do { - const prompt_question = await prompt({ - type: 'confirm', - name: 'result', - initial: true, - message: chalk.yellow('Please open this url in browser', url) - }); - - if (!prompt_question.result) { - return Promise.reject("empty response " + prompt_question.result); - } - - const code_auth_token = await fetch(OAUTH_HOST + 'code_auth_token?device_id=' + device_id + '&client_id=' + DEPLOY_APP_ID); - const code_auth_token_json = await code_auth_token.json(); - - if (code_auth_token.status !== CODE_SUCCESS) { - console.error('code_auth_token.status: ', code_auth_token.status, code_auth_token_json); - continue; - } - - const {access_token} = code_auth_token_json; - if (access_token || access_token === null) { - handled = true; - } - - return Promise.resolve(access_token); - - } while (handled === false); - } -} - -async function api(method, params) { - params['v'] = API_VERSION; - params['access_token'] = cfg.access_token; - params['cli_version'] = CLIENT_VERSION; - - if (!cfg.access_token) { - console.error('access_token is missing'); - return false; - } - - const queryParams = Object.keys(params).map((k) => { return k + "=" + encodeURIComponent(params[k]) }).join('&'); - try { - const query = await fetch(API_HOST + method + '?' + queryParams); - const res = await query.json(); - if (res.error !== void 0) { - throw new Error(chalk.red(res.error.error_code + ': ' + res.error.error_msg)); - } - - if (res.response !== void 0) { - return res.response; - } - } catch (e) { - console.error(e); - } -} - -async function upload(uploadUrl, bundleFile) { - const formData = new FormData(); - formData.append('file', fs.createReadStream(bundleFile), {contentType: 'application/zip'}); - try { - const upload = await fetch(uploadUrl, { - method: 'POST', - headers: formData.getHeaders(), - body: formData - }); - return await upload.json(); - } catch (e) { - console.error('upload error', e); - } -} - -async function handleQueue(user_id, base_url, key, ts, version, handled) { - const url = base_url + '?act=a_check&key=' + key + '&ts=' + ts + '&id=' + user_id + '&wait=5'; - const query = await fetch(url); - const res = await query.json(); - - const ciUrls = !!process.env.CI_URLS; - - if (handled === false) { - handled = { - production: false, - dev: false, - }; - } - - if (handled.production && handled.dev) { - return true; - } - - if (res.events !== void 0 && res.events.length) { - for (let i = 0; i < res.events.length; i++) { - let event = res.events[i].data; - if (event.type === 'error') { - const message = event.message || ''; - console.error(chalk.red('Deploy failed, error code: #' + event.code + ' ' + message)); - return false; - } - - if (event.type === TYPE_SUCCESS) { - if (event.code === CODE_SUCCESS) { - console.info(chalk.green('Deploy success...')); - continue; - } - - if (event.code === CODE_CONFIRM_SENT_VIA_MESSAGE) { - console.info(chalk.green('Please, confirm deploy on your phone.')); - const result = await prompt({ - type: 'text', - name: 'code', - message: chalk.yellow('Please, enter code from Administration: ') - }); - - if (result.code) { - const r = await api('apps.confirmDeploy', { - app_id: cfg.app_id, - version: version, - code: result.code - }); - if (r.error) { - console.error('Invalid confirm code'); - return false; - } - } - } - - if (event.code === CODE_PUSH_SENT_VIA_PUSH) { - console.info(chalk.green('Please, confirm deploy on your phone.')); - continue; - } - - if (event.code === CODE_PUSH_APPROVED) { - console.info(chalk.green('Deploy confirmed successfully.')); - continue; - } - - if (event.code === CODE_SKIP) { - switch (event.message.environment) { - case APPLICATION_ENV_DEV: - handled.dev = true; - break; - - case APPLICATION_ENV_PRODUCTION: - handled.production = true; - break; - } - continue; - } - - if (event.code === CODE_DEPLOY) { - if (event.message && event.message.urls && Object.keys(event.message.urls).length) { - const urls = event.message.urls; - if (event.message.is_production && !handled.production) { - handled.production = true; - console.info(chalk.green('URLs changed for production:')); - } - - if (!event.message.is_production && !handled.dev) { - handled.dev = true; - console.info(chalk.green('URLs changed for dev:')); - } - - let urlKeys = Object.keys(urls); - for (let j = 0; j < urlKeys.length; j++) { - if (urlKeys[j] === URL_NAMES.WEB_LEGACY) { - continue; - } - - let prefix = null; - if (ciUrls) { - prefix = urlKeys[j]; - } else { - prefix = URL_NAMES_MAP[urlKeys[j]]; - } - - if (prefix) { - prefix += ':\t'; - } else { - prefix = ''; - } - - console.log(prefix + urls[urlKeys[j]]); - } - } - } - } - } - } - - return handleQueue(user_id, base_url, key, res.ts, version, handled); -} - -async function getQueue(version) { - const r = await api('apps.subscribeToHostingQueue', {app_id: cfg.app_id, version: version}); - if (!r.base_url || !r.key || !r.ts || !r.app_id) { - throw new Error(JSON.stringify(r)); - } - - return handleQueue(r.app_id, r.base_url, r.key, r.ts, version, false); -} - -async function run(cfg) { - - if (!configJSON) { - throw new Error('For deploy you need to create config file "vk-hosting-config.json"'); - } - - try { - const staticPath = cfg.static_path || cfg.staticpath; - const defaultEnvironment = APPLICATION_ENV_DEV | APPLICATION_ENV_PRODUCTION; - const environmentMapping = { - dev: APPLICATION_ENV_DEV, - production: APPLICATION_ENV_PRODUCTION - }; - - const environment = process.env.MINI_APPS_ENVIRONMENT - ? (environmentMapping[process.env.MINI_APPS_ENVIRONMENT] || defaultEnvironment) - : defaultEnvironment; - - if (process.env.MINI_APPS_ACCESS_TOKEN) { - cfg.access_token = process.env.MINI_APPS_ACCESS_TOKEN; - } - - if (!cfg.access_token && vault.get('access_token')) { - cfg.access_token = vault.get('access_token'); - } - - if (!cfg.access_token) { - console.log('Try to retrieve access token...'); - const access_token = await auth(); - cfg.access_token = access_token; - vault.set('access_token', access_token); - console.log(chalk.cyan('Token is saved in config store!')); - console.log(chalk.cyan('\nFor your CI, you can use \n > $ env MINI_APPS_ACCESS_TOKEN=' + access_token + ' yarn deploy')); - } - - if (process.env.MINI_APPS_APP_ID) { - const appId = parseInt(process.env.MINI_APPS_APP_ID, 10); - if (isNaN(appId)) { - throw new Error('env MINI_APPS_APP_ID is not valid number'); - } - cfg.app_id = appId; - } - - if (!cfg.app_id) { - throw new Error('Please provide "app_id" to vk-hosting-config.json or env MINI_APPS_APP_ID'); - } - - const params = {app_id: cfg.app_id, environment: environment}; - const endpointPlatformKeys = Object.keys(cfg.endpoints); - if (endpointPlatformKeys.length) { - for (let i = 0; i < endpointPlatformKeys.length; i++) { - let endpoint = cfg.endpoints[endpointPlatformKeys[i]]; - let fileName = new URL(`/${endpoint}`, 'https://.').pathname; - let filePath = './' + staticPath + fileName; - - if (!fs.existsSync(filePath)) { - throw new Error('File ' + filePath + ' not found'); - } - params['endpoint_' + endpointPlatformKeys[i]] = endpoint; - } - } - - const r = await api('apps.getBundleUploadServer', params); - if (!r || !r.upload_url) { - throw new Error(JSON.stringify('upload_url is undefined', r)); - } - - const uploadURL = r.upload_url; - const bundleFile = cfg.bundleFile || './build.zip'; - - if (!cfg.bundleFile) { - const excludedFiles = await glob.sync('./' + staticPath + '/**/*.txt'); - - await excludedFiles.forEach((file) => { - fs.removeSync(file); - }); - - if (await fs.pathExists(bundleFile)) { - fs.removeSync(bundleFile) - } - - - await zip('./' + staticPath, bundleFile); - } - - if (!fs.pathExists(bundleFile)) { - console.error('Empty bundle file: ' + bundleFile); - return false; - } - - return await upload(uploadURL, bundleFile).then((r) => { - if (r.version) { - console.log('Uploaded version ' + r.version + '!'); - return getQueue(r.version); - } else { - console.error('Upload error:', r) - } - }); - - } catch (e) { - console.error(chalk.red(e)); - process.exit(1); - } -} - -module.exports = { - run: run -}; +require('./lib/index'); diff --git a/lib/auth.js b/lib/auth.js new file mode 100644 index 0000000..5cd5852 --- /dev/null +++ b/lib/auth.js @@ -0,0 +1,43 @@ +const prompt = require('prompts'); +const fetch = require('node-fetch'); +const chalk = require('chalk'); + +module.exports = async (config) => { + if (config.noprompt) { + throw new Error('Authorization required, but CI/CD mode is enabled'); + } + + const codeURL = config.oauth_host + 'get_auth_code?scope=offline&client_id=' + config.oauth_app; + + const authCodeResponse = await (await fetch(codeURL)).json(); + + const authCodeFail = authCodeResponse.error || authCodeResponse.response; + if (authCodeFail) { + throw new Error(JSON.stringify(authCodeFail)); + } + + const authCode = authCodeResponse.auth_code; + const authCodeDevice = authCodeResponse.device_id; + if (!authCode) { + throw new Error('Empty auth code received'); + } + + const confirmURL = config.oauth_host + 'code_auth?stage=check&code=' + authCode; + const tokenURL = config.oauth_host + 'code_auth_token?device_id=' + authCodeDevice + '&client_id=' + config.oauth_app; + + await prompt({ + type: 'invisible', + initial: '', + message: chalk.yellow('Please open this url in browser', confirmURL), + onCancel: () => true + }); + + const authTokenResponse = await (await fetch(tokenURL)).json(); + + const token = authTokenResponse.access_token; + if (!token) { + throw new Error('Empty access token received'); + } + + return token; +}; diff --git a/lib/confirm.js b/lib/confirm.js new file mode 100644 index 0000000..85e77e9 --- /dev/null +++ b/lib/confirm.js @@ -0,0 +1,29 @@ +const fetch = require('node-fetch').default; +const stringify = require('querystring').stringify; +const prompt = require('prompts'); +const chalk = require('chalk'); + +module.exports = async (config, version) => { + if (config.noprompt) { + throw new Error('Authorization required, but CI/CD mode is enabled'); + } else { + const result = await prompt({ + type: 'text', + name: 'code', + message: chalk.yellow('Please, enter code from Administration: '), + onCancel: () => true + }); + + const params = { + app_id: config.app_id, + version: version, + code: result.code, + v: config.api_version, + access_token: config.access_token + }; + const payload = await (await fetch(config.api_host + 'apps.confirmDeploy' + stringify(params))).json(); + if (payload.error || payload.response && payload.response.error) { + throw new Error('Authorization failed'); + } + } +}; diff --git a/lib/deploy.js b/lib/deploy.js new file mode 100644 index 0000000..ff87d27 --- /dev/null +++ b/lib/deploy.js @@ -0,0 +1,151 @@ +const fetch = require('node-fetch').default; +const stringify = require('querystring').stringify; +const chalk = require('chalk'); + +const confirm = require('./confirm'); + +const URL_NAMES = { + DESKTOP_DEV: 'vk_app_desktop_dev_url', + MOBILE_DEV: 'vk_app_dev_url', + MOBILE_WEB_DEV: 'vk_mini_app_mvk_dev_url', + WEB_LEGACY: 'iframe_url', + WEB: 'iframe_secure_url', + MOBILE: 'm_iframe_secure_url', + MOBILE_WEB: 'vk_mini_app_mvk_url' +}; + +const PLATFORMS = { + WEB: 'vk.com', + MOBILE: 'iOS & Android', + MOBILE_WEB: 'm.vk.com' +}; + +const URL_NAMES_MAP = { + [URL_NAMES.DESKTOP_DEV]: PLATFORMS.WEB, + [URL_NAMES.MOBILE_DEV]: PLATFORMS.MOBILE, + [URL_NAMES.MOBILE_WEB_DEV]: PLATFORMS.MOBILE_WEB, + [URL_NAMES.WEB_LEGACY]: PLATFORMS.WEB, + [URL_NAMES.WEB]: PLATFORMS.WEB, + [URL_NAMES.MOBILE]: PLATFORMS.MOBILE, + [URL_NAMES.MOBILE_WEB]: PLATFORMS.MOBILE_WEB +}; + +const PAD_LENGTH = 16; + +const ENVIRONMENT_DEV = 1; +const ENVIRONMENT_PROD = 2; + +const CODE_SUCCESS = 200; +const CODE_DEPLOY = 201; +const CODE_SKIP = 202; +const CODE_PUSH_SENT_VIA_PUSH = 203; +const CODE_PUSH_APPROVED = 204; +const CODE_CONFIRM_SENT_VIA_MESSAGE = 205; + +const CI_URLS = !!process.env.CI_URLS; + +const pull = async (config, version, server, state) => { + if (state.dev && state.prod) { + return; + } + + const pullURL = server.base_url + '?act=a_check&key=' + server.key + '&ts=' + server.ts + '&id=' + server.app_id + '&wait=5'; + const payload = await (await fetch(pullURL)).json(); + + if (payload.events) { + for (const pulled of payload.events) { + const event = pulled.data; + + if (event.type === 'error') { + const message = event.message || ''; + throw new Error('Deploy failed, error code: #' + event.code + ' ' + message); + } + + if (event.type === 'success') { + switch (event.code) { + case CODE_SUCCESS: + console.info(chalk.green('Deploy success...')); + continue; + case CODE_PUSH_SENT_VIA_PUSH: + console.info(chalk.green('Please, confirm deploy on your phone.')); + continue; + case CODE_PUSH_APPROVED: + console.info(chalk.green('Deploy confirmed successfully.')); + continue; + + case CODE_CONFIRM_SENT_VIA_MESSAGE: + await confirm(config, version); + continue; + + case CODE_SKIP: + switch (event.message.environment) { + case ENVIRONMENT_DEV: + state.dev = true; + continue; + case ENVIRONMENT_PROD: + state.prod = true; + continue; + default: + continue; + } + + case CODE_DEPLOY: { + const urls = event.message && event.message.urls; + if (urls) { + const isProduction = event.message.is_production; + + if (isProduction && !state.prod) { + state.prod = true; + console.info(chalk.green('URLs changed for production:')); + } + + if (!isProduction && !state.dev) { + state.dev = true; + console.info(chalk.green('URLs changed for dev:')); + } + + for (const name in urls) { + const url = urls[name]; + + if (url === URL_NAMES.WEB_LEGACY) { + continue; + } + + let prefix = URL_NAMES_MAP[name]; + if (CI_URLS) { + prefix = name; + } + + prefix = prefix ? (prefix + ':').padEnd(PAD_LENGTH, ' ') : ''; + console.log(prefix + url); + } + } + } + } + } + } + } + + server.ts = payload.ts; + return pull(config, version, server, state); +}; + +module.exports = async (config, version) => { + const params = { + app_id: config.app_id, + version: version, + v: config.api_version, + access_token: config.access_token + }; + + const payload = await (await fetch(config.api_host + 'apps.subscribeToHostingQueue?' + stringify(params))).json(); + const server = payload.response; + if (payload.error || !server || !server.base_url || !server.key || !server.ts || !server.app_id) { + throw new Error('Unfortunately, the server is temporarily unavailable. Please try again later.'); + } + + return pull(config, version, server, { + dev: false, + prod: false + }); +}; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..5582cf5 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,106 @@ +const path = require('path'); +const fs = require('fs').promises; +const access = require('fs').constants; +const chalk = require('chalk'); +const isCI = require('is-ci'); +const Configstore = require('configstore'); +const prompt = require('prompts'); + +const pkg = require('../package.json'); + +const auth = require('./auth'); +const upload = require('./upload'); +const deploy = require('./deploy'); + +const ENVIRONMENT_DEV = 1; +const ENVIRONMENT_PROD = 2; +const ENVIRONMENTS = { + dev: ENVIRONMENT_DEV, + development: ENVIRONMENT_DEV, + + prod: ENVIRONMENT_PROD, + production: ENVIRONMENT_PROD +}; + +const DEFAULTS = { + app_id: 0, + + api_host: 'https://api.vk.com/method/', + api_version: '5.131', + + oauth_host: 'https://oauth.vk.com/', + oauth_app: 6670517, + + noprompt: isCI, + access_token: '', + + environment: ENVIRONMENT_DEV +}; + +(async () => { + try { + // config + const configPath = path.resolve(process.cwd(), 'vk-hosting-config.json'); + await fs.access(configPath, access.R_OK); + const config = Object.assign(DEFAULTS, require(configPath)); + + // vault + const vault = config.noprompt ? new Map() : new Configstore(pkg.name, {}); + + // files + config.static_path = process.env.MINI_APPS_PATH || config.static_path || config.staticpath || 'dist'; + config.static_path = path.resolve(process.cwd(), config.static_path); + await fs.access(config.static_path, access.R_OK); + + // app_id + config.app_id = process.env.MINI_APPS_APP_ID || config.app_id; + if (!config.app_id) { + throw new Error('Please provide "app_id" to vk-hosting-config.json or env MINI_APPS_APP_ID'); + } + + // environment + config.environment = process.env.MINI_APPS_ENVIRONMENT && + ENVIRONMENTS[process.env.MINI_APPS_ENVIRONMENT] || config.environment; + + // access_token + config.access_token = process.env.MINI_APPS_ACCESS_TOKEN || config.access_token || vault.get('access_token'); + if (!config.access_token) { + console.log('Try to retrieve access token...'); + config.access_token = await auth(config); + } + if (!config.noprompt) { + const saved = vault.get('access_token'); + vault.set('access_token', config.access_token); + if (!saved) { + console.log(chalk.cyan('Token is saved in config store!')); + console.log(chalk.cyan('\nFor your CI, you can use \n > $ cross-env MINI_APPS_ACCESS_TOKEN=' + config.access_token + ' yarn deploy')); + } + } + + if (!config.noprompt) { + const result = await prompt({ + type: 'confirm', + initial: true, + name: 'confirm', + message: chalk.yellow('Would you like to deploy to VK Mini Apps hosting using these commands?') + }); + + if (!result.confirm) { + return; + } + } + + const version = await upload(config); + console.log('Uploaded version ' + version + '!'); + + await deploy(config, version); + } catch (e) { + if (e && e.syscall === 'access') { + const file = path.basename(e.path); + console.error(chalk.red(file + ' is missing or unreadable')); + } else { + console.error(chalk.red(e && e.message || 'Unknown error occured')); + process.exit(1); + } + } +})(); diff --git a/lib/upload.js b/lib/upload.js new file mode 100644 index 0000000..726cba9 --- /dev/null +++ b/lib/upload.js @@ -0,0 +1,59 @@ +const path = require('path'); +const fs = require('fs').promises; +const access = require('fs').constants; +const fetch = require('node-fetch').default; +const stringify = require('querystring').stringify; +const { zip } = require('zip-a-folder'); +const util = require('util'); +const stream = require('fs'); +const FormData = require('form-data'); + +const compress = util.promisify(zip); + +const CLIENT_VERSION = 2; + +module.exports = async (config) => { + const params = { + app_id: config.app_id, + environment: config.environment, + cli_version: CLIENT_VERSION, + v: config.api_version, + access_token: config.access_token + }; + + for (const endpoint in config.endpoints) { + const fileName = config.endpoints[endpoint]; + const filePath = path.resolve(config.static_path, fileName); + + await fs.access(filePath, access.R_OK); + + params['endpoint_' + endpoint] = config.endpoints[endpoint]; + } + + const payload = await (await fetch(config.api_host + 'apps.getBundleUploadServer' + '?' + stringify(params))).json(); + const uploadURL = payload.response && payload.response.upload_url; + if (payload.error || !uploadURL) { + throw new Error('Unfortunately, the server is temporarily unavailable. Please try again later.'); + } + + const preBundlePath = config.bundle_file || config.bundleFile; + const bundlePath = path.resolve(process.cwd(), preBundlePath || 'build.zip'); + if (preBundlePath) { + await fs.access(bundlePath, access.R_OK); + } else { + await compress(config.static_path, bundlePath); + } + + const formData = new FormData(); + formData.append('file', stream.createReadStream(bundlePath), { contentType: 'application/zip' }); + const upload = await (await fetch(uploadURL, { + method: 'POST', + headers: formData.getHeaders(), + body: formData + })).json(); + if (!upload.version) { + throw new Error('Unfortunately, the server is temporarily unavailable. Please try again later.'); + } + + return upload.version; +}; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index ac332ad..0000000 --- a/package-lock.json +++ /dev/null @@ -1,618 +0,0 @@ -{ - "name": "@vkontakte/vk-miniapps-deploy", - "version": "0.0.21", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "archiver": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz", - "integrity": "sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==", - "requires": { - "archiver-utils": "^2.1.0", - "async": "^2.6.3", - "buffer-crc32": "^0.2.1", - "glob": "^7.1.4", - "readable-stream": "^3.4.0", - "tar-stream": "^2.1.0", - "zip-stream": "^2.1.2" - }, - "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - } - } - }, - "archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "requires": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } - } - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" - }, - "bl": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "compress-commons": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz", - "integrity": "sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==", - "requires": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^3.0.1", - "normalize-path": "^3.0.0", - "readable-stream": "^2.3.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "configstore": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.0.tgz", - "integrity": "sha512-eE/hvMs7qw7DlcB5JPRnthmrITuHMmACUJAp89v6PT6iOqzoLS7HRWhBtuHMlhNHo2AhUSA/3Dh1bKNJHcublQ==", - "requires": { - "dot-prop": "^5.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "requires": { - "buffer": "^5.1.0" - } - }, - "crc32-stream": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz", - "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==", - "requires": { - "crc": "^3.4.4", - "readable-stream": "^3.4.0" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "dot-prop": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.1.1.tgz", - "integrity": "sha512-QCHI6Lkf+9fJMpwfAFsTvbiSh6ujoPmhCLiDvD/n4dGtLvHfhuBwPdN6z2x4YSOwwtTcLoO/LP70xELWGF/JVA==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "fs-extra": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.0.1.tgz", - "integrity": "sha512-W+XLrggcDzlle47X/XnS7FXrXu9sDo+Ze9zpndeBxdgv88FHLm1HtmkhEwavruS6koanBjp098rUpHs65EmG7A==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", - "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" - }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "requires": { - "readable-stream": "^2.0.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" - }, - "make-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", - "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", - "requires": { - "semver": "^6.0.0" - } - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "prompts": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.1.0.tgz", - "integrity": "sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==", - "requires": { - "kleur": "^3.0.2", - "sisteransi": "^1.0.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "require-module": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/require-module/-/require-module-0.1.0.tgz", - "integrity": "sha1-YwfrWsHYJZQmoiUVdTZWGOGRUT4=", - "requires": { - "resolve": "~0.6.1" - }, - "dependencies": { - "resolve": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", - "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=" - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "sisteransi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.2.tgz", - "integrity": "sha512-ZcYcZcT69nSLAR2oLN2JwNmLkJEKGooFMCdvOkFrToUt/WfcRWqhIg4P4KwY4dmLbuyXIx4o4YmPsvMRJYJd/w==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "tar-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz", - "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==", - "requires": { - "bl": "^4.0.1", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", - "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" - }, - "zip-a-folder": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/zip-a-folder/-/zip-a-folder-0.0.12.tgz", - "integrity": "sha512-wZGiWgp3z2TocBlzx3S5tsLgPbT39qG2uIZmn2MhYLVjhKIr2nMhg7i4iPDL4W3XvMDaOEEVU5ZB0Y/Pt6BLvA==", - "requires": { - "archiver": "^3.1.1" - } - }, - "zip-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz", - "integrity": "sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q==", - "requires": { - "archiver-utils": "^2.1.0", - "compress-commons": "^2.1.1", - "readable-stream": "^3.4.0" - } - } - } -} diff --git a/package.json b/package.json index 55fe71b..e95209b 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "@vkontakte/vk-miniapps-deploy", - "version": "0.0.25", + "version": "0.0.26", "description": "Deploy to VK Mini Apps hosting with one simple command", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "start": "./bin/vk-miniapps-deploy", "deploy": "./bin/vk-miniapps-deploy" }, "license": "MIT", "engines": { - "node": ">=8.10" + "node": ">=10.17" }, "author": { "name": "VK", @@ -33,15 +33,17 @@ }, "homepage": "https://github.com/vkcom/vk-miniapps-deploy", "dependencies": { - "async": "^3.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.0", - "form-data": "^3.0.0", - "fs-extra": "^8.0.1", - "glob": "^7.1.6", - "node-fetch": "^2.6.0", - "prompts": "^2.1.0", - "require-module": "^0.1.0", - "zip-a-folder": "0.0.12" + "chalk": "^4.1.1", + "configstore": "^5.0.1", + "form-data": "^4.0.0", + "is-ci": "^3.0.0", + "node-fetch": "^2.6.1", + "prompts": "^2.4.1", + "zip-a-folder": "1.0.1" + }, + "devDependencies": { + "@types/node": "^15.3.0", + "@vkontakte/eslint-config": "^3.0.0", + "eslint": "^7.26.0" } -} +} \ No newline at end of file From 149a9438e325cd41293d76dca26d0291f27f47b9 Mon Sep 17 00:00:00 2001 From: eolme Date: Sun, 16 May 2021 20:48:44 +0400 Subject: [PATCH 02/21] Fix default environment --- lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index 5582cf5..3449718 100644 --- a/lib/index.js +++ b/lib/index.js @@ -34,7 +34,7 @@ const DEFAULTS = { noprompt: isCI, access_token: '', - environment: ENVIRONMENT_DEV + environment: ENVIRONMENT_DEV | ENVIRONMENT_PROD }; (async () => { From 0987205cfa0b8520851c4ff4b535d1dcc59afbe9 Mon Sep 17 00:00:00 2001 From: eolme Date: Sun, 16 May 2021 20:49:03 +0400 Subject: [PATCH 03/21] Fix exit on error --- lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index 3449718..e35f6fc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -100,7 +100,7 @@ const DEFAULTS = { console.error(chalk.red(file + ' is missing or unreadable')); } else { console.error(chalk.red(e && e.message || 'Unknown error occured')); - process.exit(1); } + process.exit(1); } })(); From 74ec8761acc63029f78ceffd68745bf593911e45 Mon Sep 17 00:00:00 2001 From: eolme Date: Sun, 16 May 2021 20:56:32 +0400 Subject: [PATCH 04/21] Fix zip compress --- lib/upload.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/upload.js b/lib/upload.js index 726cba9..09d880a 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -4,12 +4,9 @@ const access = require('fs').constants; const fetch = require('node-fetch').default; const stringify = require('querystring').stringify; const { zip } = require('zip-a-folder'); -const util = require('util'); const stream = require('fs'); const FormData = require('form-data'); -const compress = util.promisify(zip); - const CLIENT_VERSION = 2; module.exports = async (config) => { @@ -41,7 +38,7 @@ module.exports = async (config) => { if (preBundlePath) { await fs.access(bundlePath, access.R_OK); } else { - await compress(config.static_path, bundlePath); + await zip(config.static_path, bundlePath); } const formData = new FormData(); From ad89af5d8052ebe157cbd07101d30c16dbd26e6d Mon Sep 17 00:00:00 2001 From: eolme Date: Sun, 16 May 2021 20:56:49 +0400 Subject: [PATCH 05/21] Fix legacy skip --- lib/deploy.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/deploy.js b/lib/deploy.js index ff87d27..314af96 100644 --- a/lib/deploy.js +++ b/lib/deploy.js @@ -105,12 +105,12 @@ const pull = async (config, version, server, state) => { } for (const name in urls) { - const url = urls[name]; - - if (url === URL_NAMES.WEB_LEGACY) { + if (name === URL_NAMES.WEB_LEGACY) { continue; } + const url = urls[name]; + let prefix = URL_NAMES_MAP[name]; if (CI_URLS) { prefix = name; From ba7b72067072ce2ff3d1686c0c054c5ab50ea2ea Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 10 Aug 2021 16:08:35 +0400 Subject: [PATCH 06/21] Unwrap else statement --- lib/confirm.js | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/confirm.js b/lib/confirm.js index 85e77e9..64fd381 100644 --- a/lib/confirm.js +++ b/lib/confirm.js @@ -6,24 +6,24 @@ const chalk = require('chalk'); module.exports = async (config, version) => { if (config.noprompt) { throw new Error('Authorization required, but CI/CD mode is enabled'); - } else { - const result = await prompt({ - type: 'text', - name: 'code', - message: chalk.yellow('Please, enter code from Administration: '), - onCancel: () => true - }); + } + + const result = await prompt({ + type: 'text', + name: 'code', + message: chalk.yellow('Please, enter code from Administration: '), + onCancel: () => true + }); - const params = { - app_id: config.app_id, - version: version, - code: result.code, - v: config.api_version, - access_token: config.access_token - }; - const payload = await (await fetch(config.api_host + 'apps.confirmDeploy' + stringify(params))).json(); - if (payload.error || payload.response && payload.response.error) { - throw new Error('Authorization failed'); - } + const params = { + app_id: config.app_id, + version: version, + code: result.code, + v: config.api_version, + access_token: config.access_token + }; + const payload = await (await fetch(config.api_host + 'apps.confirmDeploy' + stringify(params))).json(); + if (payload.error || payload.response && payload.response.error) { + throw new Error('Authorization failed'); } }; From 7aa9939d01dd17b5f7a23024f64338ab6b6aae52 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 10 Aug 2021 16:08:48 +0400 Subject: [PATCH 07/21] Fix typo --- lib/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index e35f6fc..c5ab9ba 100644 --- a/lib/index.js +++ b/lib/index.js @@ -31,7 +31,7 @@ const DEFAULTS = { oauth_host: 'https://oauth.vk.com/', oauth_app: 6670517, - noprompt: isCI, + noprompt: isCI || !process.stdout.isTTY, access_token: '', environment: ENVIRONMENT_DEV | ENVIRONMENT_PROD @@ -99,7 +99,7 @@ const DEFAULTS = { const file = path.basename(e.path); console.error(chalk.red(file + ' is missing or unreadable')); } else { - console.error(chalk.red(e && e.message || 'Unknown error occured')); + console.error(chalk.red(e && e.message || 'Unknown error occurred')); } process.exit(1); } From c41fca9af2f7b6d68fd6d973a9e2d98377659409 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 10 Aug 2021 16:09:24 +0400 Subject: [PATCH 08/21] Use recommended packages --- lib/upload.js | 23 +++++++++++++++++------ package.json | 12 +++++++----- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/upload.js b/lib/upload.js index 09d880a..c158082 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -4,8 +4,9 @@ const access = require('fs').constants; const fetch = require('node-fetch').default; const stringify = require('querystring').stringify; const { zip } = require('zip-a-folder'); -const stream = require('fs'); -const FormData = require('form-data'); +const { FormData, fileFromPath } = require('formdata-node'); +const { Encoder } = require('form-data-encoder'); +const { Readable } = require('stream'); const CLIENT_VERSION = 2; @@ -38,15 +39,25 @@ module.exports = async (config) => { if (preBundlePath) { await fs.access(bundlePath, access.R_OK); } else { - await zip(config.static_path, bundlePath); + const error = await zip(config.static_path, bundlePath, { compression: 0 }); + if (error) { + throw error; + } } + const file = await fileFromPath(bundlePath); const formData = new FormData(); - formData.append('file', stream.createReadStream(bundlePath), { contentType: 'application/zip' }); + formData.append('file', file, { + type: 'application/zip', + filename: path.basename(bundlePath) + }); + + const encoder = new Encoder(formData); + const upload = await (await fetch(uploadURL, { method: 'POST', - headers: formData.getHeaders(), - body: formData + headers: encoder.headers, + body: Readable.from(encoder) })).json(); if (!upload.version) { throw new Error('Unfortunately, the server is temporarily unavailable. Please try again later.'); diff --git a/package.json b/package.json index e95209b..ac19489 100644 --- a/package.json +++ b/package.json @@ -33,17 +33,19 @@ }, "homepage": "https://github.com/vkcom/vk-miniapps-deploy", "dependencies": { - "chalk": "^4.1.1", + "chalk": "^4.1.2", "configstore": "^5.0.1", - "form-data": "^4.0.0", + "fetch-blob": "^3.1.2", + "form-data-encoder": "^1.4.1", + "formdata-node": "^3.6.3", "is-ci": "^3.0.0", "node-fetch": "^2.6.1", "prompts": "^2.4.1", - "zip-a-folder": "1.0.1" + "zip-a-folder": "0.0.12" }, "devDependencies": { - "@types/node": "^15.3.0", + "@types/node": "^16.4.13", "@vkontakte/eslint-config": "^3.0.0", - "eslint": "^7.26.0" + "eslint": "^7.32.0" } } \ No newline at end of file From 1a369e4b51ff7ef39d7fa47414dd850b224483af Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 10 Aug 2021 16:19:12 +0400 Subject: [PATCH 09/21] Add docs --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d9d0e5..3a57052 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,16 @@ To configure `vk-miniapps-deploy` all you need to do is specify a couple of thin and remove the suffix «.example» * Run yarn deploy -For your CI, you can use +CI/CD is automatically detected, you only need to pass `access_token` to env or define in config: ```bash -$ env MINI_APPS_ACCESS_TOKEN= yarn deploy +$ cross-env MINI_APPS_ACCESS_TOKEN= yarn deploy +``` +or +```json +{ + "access_token": "" +} ``` with *user token* retrieved from vk-miniapps-deploy OR *service token* of deployable application @@ -68,6 +74,13 @@ All production builds will be also deployed on dev environment. If you grep URL paths, you can use environment variable `CI_URLS = true`. +If you always need to run in CI/CD-mode, in config: +```json +{ + "noprompt": true +} +``` + ## Troubleshooting: If you get an error `User authorization failed: invalid session`, try this comand: ```bash From fced8509612768e3fcf3c84a25d44a7071fcdb16 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 10 Aug 2021 17:35:02 +0400 Subject: [PATCH 10/21] Show more informative errors --- README.md | 6 +++++- lib/assert.js | 37 +++++++++++++++++++++++++++++++++++++ lib/auth.js | 17 +++++++---------- lib/confirm.js | 7 ++++--- lib/deploy.js | 5 ++++- lib/index.js | 15 +++++++++++++-- lib/upload.js | 8 +++++++- package.json | 5 +++++ 8 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 lib/assert.js diff --git a/README.md b/README.md index 3a57052..dee2d09 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ To configure `vk-miniapps-deploy` all you need to do is specify a couple of thin ``` ## How to use: + * Make sure that in package.json the key value «homepage» is «./» * Copy the example config to the root folder of your application vk-hosting-config.json.example and remove the suffix «.example» @@ -82,10 +83,13 @@ If you always need to run in CI/CD-mode, in config: ``` ## Troubleshooting: -If you get an error `User authorization failed: invalid session`, try this comand: + +If you get an error `Access key is invalid`, try this comand: + ```bash rm ~/.config/configstore/@vkontakte/vk-miniapps-deploy.json ``` + [npm]: https://img.shields.io/npm/v/@vkontakte/vk-miniapps-deploy.svg [npm-url]: https://npmjs.com/package/@vkontakte/vk-miniapps-deploy [deps]: https://img.shields.io/david/vkcom/vk-miniapps-deploy.svg diff --git a/lib/assert.js b/lib/assert.js new file mode 100644 index 0000000..8d08cae --- /dev/null +++ b/lib/assert.js @@ -0,0 +1,37 @@ +const getInnerMessage = (inner) => { + if (inner.message) { + return inner.message; + } + if (inner.error_msg) { + return inner.error_msg; + } + if (inner.error_message) { + return inner.error_message; + } + return 'Unknown error occurred'; +}; + +const getMessage = (payload) => { + if (!payload) { + return 'Unknown error occurred'; + } + if (typeof payload === 'string') { + return payload; + } + if (payload.error) { + return getInnerMessage(payload.error); + } + if (payload.response && payload.response.error) { + return getInnerMessage(payload.response.error); + } + return null; +}; + +const assert = (payload) => { + const message = getMessage(payload); + if (message !== null) { + throw new Error(message); + } +}; + +module.exports = assert; diff --git a/lib/auth.js b/lib/auth.js index 5cd5852..e6e25c1 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -1,6 +1,9 @@ const prompt = require('prompts'); const fetch = require('node-fetch'); const chalk = require('chalk'); +const link = require('terminal-link'); + +const assert = require('./assert'); module.exports = async (config) => { if (config.noprompt) { @@ -10,17 +13,10 @@ module.exports = async (config) => { const codeURL = config.oauth_host + 'get_auth_code?scope=offline&client_id=' + config.oauth_app; const authCodeResponse = await (await fetch(codeURL)).json(); - - const authCodeFail = authCodeResponse.error || authCodeResponse.response; - if (authCodeFail) { - throw new Error(JSON.stringify(authCodeFail)); - } + assert(authCodeResponse); const authCode = authCodeResponse.auth_code; const authCodeDevice = authCodeResponse.device_id; - if (!authCode) { - throw new Error('Empty auth code received'); - } const confirmURL = config.oauth_host + 'code_auth?stage=check&code=' + authCode; const tokenURL = config.oauth_host + 'code_auth_token?device_id=' + authCodeDevice + '&client_id=' + config.oauth_app; @@ -28,15 +24,16 @@ module.exports = async (config) => { await prompt({ type: 'invisible', initial: '', - message: chalk.yellow('Please open this url in browser', confirmURL), + message: chalk.yellow('Please open this', link('url', confirmURL), 'in browser'), onCancel: () => true }); const authTokenResponse = await (await fetch(tokenURL)).json(); + assert(authTokenResponse); const token = authTokenResponse.access_token; if (!token) { - throw new Error('Empty access token received'); + throw new Error('User authorization failed.'); } return token; diff --git a/lib/confirm.js b/lib/confirm.js index 64fd381..d50d1ec 100644 --- a/lib/confirm.js +++ b/lib/confirm.js @@ -3,6 +3,8 @@ const stringify = require('querystring').stringify; const prompt = require('prompts'); const chalk = require('chalk'); +const assert = require('./assert'); + module.exports = async (config, version) => { if (config.noprompt) { throw new Error('Authorization required, but CI/CD mode is enabled'); @@ -22,8 +24,7 @@ module.exports = async (config, version) => { v: config.api_version, access_token: config.access_token }; + const payload = await (await fetch(config.api_host + 'apps.confirmDeploy' + stringify(params))).json(); - if (payload.error || payload.response && payload.response.error) { - throw new Error('Authorization failed'); - } + assert(payload); }; diff --git a/lib/deploy.js b/lib/deploy.js index 314af96..ad20369 100644 --- a/lib/deploy.js +++ b/lib/deploy.js @@ -2,6 +2,7 @@ const fetch = require('node-fetch').default; const stringify = require('querystring').stringify; const chalk = require('chalk'); +const assert = require('./assert'); const confirm = require('./confirm'); const URL_NAMES = { @@ -139,8 +140,10 @@ module.exports = async (config, version) => { }; const payload = await (await fetch(config.api_host + 'apps.subscribeToHostingQueue?' + stringify(params))).json(); + assert(payload); + const server = payload.response; - if (payload.error || !server || !server.base_url || !server.key || !server.ts || !server.app_id) { + if (!server || !server.base_url || !server.key || !server.ts || !server.app_id) { throw new Error('Unfortunately, the server is temporarily unavailable. Please try again later.'); } diff --git a/lib/index.js b/lib/index.js index c5ab9ba..a96696f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,6 +2,7 @@ const path = require('path'); const fs = require('fs').promises; const access = require('fs').constants; const chalk = require('chalk'); +const link = require('terminal-link'); const isCI = require('is-ci'); const Configstore = require('configstore'); const prompt = require('prompts'); @@ -38,6 +39,8 @@ const DEFAULTS = { }; (async () => { + let vault; + try { // config const configPath = path.resolve(process.cwd(), 'vk-hosting-config.json'); @@ -45,7 +48,7 @@ const DEFAULTS = { const config = Object.assign(DEFAULTS, require(configPath)); // vault - const vault = config.noprompt ? new Map() : new Configstore(pkg.name, {}); + vault = config.noprompt ? new Map() : new Configstore(pkg.name, {}); // files config.static_path = process.env.MINI_APPS_PATH || config.static_path || config.staticpath || 'dist'; @@ -99,7 +102,15 @@ const DEFAULTS = { const file = path.basename(e.path); console.error(chalk.red(file + ' is missing or unreadable')); } else { - console.error(chalk.red(e && e.message || 'Unknown error occurred')); + const message = e && e.message || 'Unknown error occurred'; + if (message.includes('authorization')) { + console.error(chalk.red('Access key is invalid.')); + if (vault) { + vault.delete('access_token'); + } + } + console.error(chalk.red(message)); + console.log('Try to run again or see ', link('https://github.com/vkcom/vk-miniapps-deploy#troubleshooting'), 'if problem recurs.'); } process.exit(1); } diff --git a/lib/upload.js b/lib/upload.js index c158082..f18b631 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -8,6 +8,8 @@ const { FormData, fileFromPath } = require('formdata-node'); const { Encoder } = require('form-data-encoder'); const { Readable } = require('stream'); +const assert = require('./assert'); + const CLIENT_VERSION = 2; module.exports = async (config) => { @@ -29,8 +31,10 @@ module.exports = async (config) => { } const payload = await (await fetch(config.api_host + 'apps.getBundleUploadServer' + '?' + stringify(params))).json(); + assert(payload); + const uploadURL = payload.response && payload.response.upload_url; - if (payload.error || !uploadURL) { + if (!uploadURL) { throw new Error('Unfortunately, the server is temporarily unavailable. Please try again later.'); } @@ -59,6 +63,8 @@ module.exports = async (config) => { headers: encoder.headers, body: Readable.from(encoder) })).json(); + assert(payload); + if (!upload.version) { throw new Error('Unfortunately, the server is temporarily unavailable. Please try again later.'); } diff --git a/package.json b/package.json index ac19489..1269009 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,10 @@ "url": "https://vk.com" }, "contributors": [ + { + "name": "Anton Petrov", + "url": "https://vk.com/petrov.engineer" + }, { "name": "Ilya Egorov", "url": "https://vk.com/mobyman" @@ -41,6 +45,7 @@ "is-ci": "^3.0.0", "node-fetch": "^2.6.1", "prompts": "^2.4.1", + "terminal-link": "^2.1.1", "zip-a-folder": "0.0.12" }, "devDependencies": { From cdbca5d8c79ec19470e3e40226a13c244ecfd695 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 10 Aug 2021 17:39:18 +0400 Subject: [PATCH 11/21] Fix link --- lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index a96696f..9603915 100644 --- a/lib/index.js +++ b/lib/index.js @@ -110,7 +110,7 @@ const DEFAULTS = { } } console.error(chalk.red(message)); - console.log('Try to run again or see ', link('https://github.com/vkcom/vk-miniapps-deploy#troubleshooting'), 'if problem recurs.'); + console.log('Try to run again or see', link('troubleshooting', 'https://github.com/vkcom/vk-miniapps-deploy#troubleshooting'), 'if problem recurs.'); } process.exit(1); } From be2064f699cfaa58c7b2b27c8e3ca4e2bd9a4f0e Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 10 Aug 2021 17:42:29 +0400 Subject: [PATCH 12/21] Fix lint --- lib/assert.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/assert.js b/lib/assert.js index 8d08cae..bb5ce18 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -1,5 +1,5 @@ const getInnerMessage = (inner) => { - if (inner.message) { + if (inner.message) { return inner.message; } if (inner.error_msg) { From 4f5a232920324488eee805dc663a6700a9d2ab95 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 10 Aug 2021 17:58:30 +0400 Subject: [PATCH 13/21] Show pure url as fallback --- lib/auth.js | 4 +++- lib/index.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/auth.js b/lib/auth.js index e6e25c1..ac77e27 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -24,7 +24,9 @@ module.exports = async (config) => { await prompt({ type: 'invisible', initial: '', - message: chalk.yellow('Please open this', link('url', confirmURL), 'in browser'), + message: chalk.yellow('Please open this', link('url', confirmURL, { + fallback: (_, url) => url + }), 'in browser'), onCancel: () => true }); diff --git a/lib/index.js b/lib/index.js index 9603915..dc89aee 100644 --- a/lib/index.js +++ b/lib/index.js @@ -110,7 +110,9 @@ const DEFAULTS = { } } console.error(chalk.red(message)); - console.log('Try to run again or see', link('troubleshooting', 'https://github.com/vkcom/vk-miniapps-deploy#troubleshooting'), 'if problem recurs.'); + console.log('Try to run again or see', link('troubleshooting', 'https://github.com/vkcom/vk-miniapps-deploy#troubleshooting', { + fallback: (_, url) => url + }), 'if problem recurs.'); } process.exit(1); } From 1e96d9490b14b7687a5b12bfaeebb1f96048aa76 Mon Sep 17 00:00:00 2001 From: Anton Petrov Date: Tue, 10 Aug 2021 18:58:28 +0400 Subject: [PATCH 14/21] Remove scripts --- package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index 1269009..604c8e8 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,6 @@ "version": "0.0.26", "description": "Deploy to VK Mini Apps hosting with one simple command", "main": "index.js", - "scripts": { - "start": "./bin/vk-miniapps-deploy", - "deploy": "./bin/vk-miniapps-deploy" - }, "license": "MIT", "engines": { "node": ">=10.17" @@ -53,4 +49,4 @@ "@vkontakte/eslint-config": "^3.0.0", "eslint": "^7.32.0" } -} \ No newline at end of file +} From 4bb5a0449f7cbb1d6920905fb0a6f43349867dd5 Mon Sep 17 00:00:00 2001 From: eolme Date: Wed, 11 Aug 2021 14:44:32 +0400 Subject: [PATCH 15/21] Inner error can be string --- lib/assert.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/assert.js b/lib/assert.js index bb5ce18..e873075 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -1,4 +1,7 @@ const getInnerMessage = (inner) => { + if (typeof inner === 'string') { + return inner; + } if (inner.message) { return inner.message; } From bc47d066c48fab665d4eaea8864ff2e9ff25158f Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 19 Oct 2021 18:43:15 +0400 Subject: [PATCH 16/21] Fix params --- lib/confirm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/confirm.js b/lib/confirm.js index d50d1ec..238ca4f 100644 --- a/lib/confirm.js +++ b/lib/confirm.js @@ -25,6 +25,6 @@ module.exports = async (config, version) => { access_token: config.access_token }; - const payload = await (await fetch(config.api_host + 'apps.confirmDeploy' + stringify(params))).json(); + const payload = await (await fetch(config.api_host + 'apps.confirmDeploy?' + stringify(params))).json(); assert(payload); }; From a060c03d5c9acfc8768996883df27153063a7024 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 19 Oct 2021 19:08:19 +0400 Subject: [PATCH 17/21] Implement debug --- lib/assert.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/assert.js b/lib/assert.js index e873075..b725b8b 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -1,3 +1,9 @@ +const util = require('util'); + +const debug = + typeof v8debug !== 'undefined' || + 'DEBUG' in process.env; + const getInnerMessage = (inner) => { if (typeof inner === 'string') { return inner; @@ -33,6 +39,15 @@ const getMessage = (payload) => { const assert = (payload) => { const message = getMessage(payload); if (message !== null) { + if (debug) { + console.log(util.inspect(payload, { + depth: null, + compact: false + })); + } else { + Error.stackTraceLimit = -1; + } + throw new Error(message); } }; From d1c6bb882464e09faaa789149e913c5d1d372f05 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 8 Feb 2022 15:05:42 +0400 Subject: [PATCH 18/21] Update --- README.md | 6 +++++- lib/deploy.js | 13 ++++++++----- lib/index.js | 8 ++++++-- lib/upload.js | 23 +++++++++++++---------- package.json | 29 +++++++++++++++-------------- 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index dee2d09..aee8b63 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,10 @@ There are two values to specify MINI_APPS_ENVIRONMENT: `production` or `dev`. All production builds will be also deployed on dev environment. If you grep URL paths, you can use environment variable `CI_URLS = true`. +URLs will be printed as followed structure: +```shell + +``` If you always need to run in CI/CD-mode, in config: ```json @@ -84,7 +88,7 @@ If you always need to run in CI/CD-mode, in config: ## Troubleshooting: -If you get an error `Access key is invalid`, try this comand: +If you get an error `Access token is invalid`, try this comand: ```bash rm ~/.config/configstore/@vkontakte/vk-miniapps-deploy.json diff --git a/lib/deploy.js b/lib/deploy.js index ad20369..5c7965d 100644 --- a/lib/deploy.js +++ b/lib/deploy.js @@ -43,7 +43,7 @@ const CODE_PUSH_SENT_VIA_PUSH = 203; const CODE_PUSH_APPROVED = 204; const CODE_CONFIRM_SENT_VIA_MESSAGE = 205; -const CI_URLS = !!process.env.CI_URLS; +const CI_URLS = process.env.CI_URLS === 'true' || process.env.CI_URLS === '1'; const pull = async (config, version, server, state) => { if (state.dev && state.prod) { @@ -112,12 +112,15 @@ const pull = async (config, version, server, state) => { const url = urls[name]; - let prefix = URL_NAMES_MAP[name]; - if (CI_URLS) { - prefix = name; + let prefix = ''; + + if (!CI_URLS) { + prefix = name in URL_NAMES_MAP ? (URL_NAMES_MAP[name] + ':').padEnd(PAD_LENGTH, ' ') : prefix; + } else { + // Backward compatible + prefix = name + ':\t'; } - prefix = prefix ? (prefix + ':').padEnd(PAD_LENGTH, ' ') : ''; console.log(prefix + url); } } diff --git a/lib/index.js b/lib/index.js index dc89aee..c8dbe6c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -32,7 +32,7 @@ const DEFAULTS = { oauth_host: 'https://oauth.vk.com/', oauth_app: 6670517, - noprompt: isCI || !process.stdout.isTTY, + noprompt: false, access_token: '', environment: ENVIRONMENT_DEV | ENVIRONMENT_PROD @@ -47,6 +47,10 @@ const DEFAULTS = { await fs.access(configPath, access.R_OK); const config = Object.assign(DEFAULTS, require(configPath)); + // CI + process.env.CI = config.noprompt || isCI || process.env.CI === '1' || process.env.CI === 'true' || !process.stdout.isTTY; + config.noprompt = process.env.CI === 'true'; + // vault vault = config.noprompt ? new Map() : new Configstore(pkg.name, {}); @@ -104,7 +108,7 @@ const DEFAULTS = { } else { const message = e && e.message || 'Unknown error occurred'; if (message.includes('authorization')) { - console.error(chalk.red('Access key is invalid.')); + console.error(chalk.red('Access token is invalid.')); if (vault) { vault.delete('access_token'); } diff --git a/lib/upload.js b/lib/upload.js index f18b631..5c987c2 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -4,8 +4,9 @@ const access = require('fs').constants; const fetch = require('node-fetch').default; const stringify = require('querystring').stringify; const { zip } = require('zip-a-folder'); -const { FormData, fileFromPath } = require('formdata-node'); -const { Encoder } = require('form-data-encoder'); +const { FormData } = require('formdata-node'); +const { fileFromPath } = require('formdata-node/file-from-path'); +const { FormDataEncoder } = require('form-data-encoder'); const { Readable } = require('stream'); const assert = require('./assert'); @@ -30,7 +31,7 @@ module.exports = async (config) => { params['endpoint_' + endpoint] = config.endpoints[endpoint]; } - const payload = await (await fetch(config.api_host + 'apps.getBundleUploadServer' + '?' + stringify(params))).json(); + const payload = await (await fetch(config.api_host + 'apps.getBundleUploadServer?' + stringify(params))).json(); assert(payload); const uploadURL = payload.response && payload.response.upload_url; @@ -49,21 +50,23 @@ module.exports = async (config) => { } } - const file = await fileFromPath(bundlePath); - const formData = new FormData(); - formData.append('file', file, { - type: 'application/zip', - filename: path.basename(bundlePath) + const fileName = path.basename(bundlePath); + const file = await fileFromPath(bundlePath, fileName, { + type: 'application/zip' }); - const encoder = new Encoder(formData); + const formData = new FormData(); + + formData.append('file', file, fileName); + + const encoder = new FormDataEncoder(formData); const upload = await (await fetch(uploadURL, { method: 'POST', headers: encoder.headers, body: Readable.from(encoder) })).json(); - assert(payload); + assert(upload); if (!upload.version) { throw new Error('Unfortunately, the server is temporarily unavailable. Please try again later.'); diff --git a/package.json b/package.json index 604c8e8..8f09579 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "@vkontakte/vk-miniapps-deploy", - "version": "0.0.26", + "version": "0.1.0", "description": "Deploy to VK Mini Apps hosting with one simple command", "main": "index.js", + "type": "commonjs", "license": "MIT", "engines": { "node": ">=10.17" @@ -33,20 +34,20 @@ }, "homepage": "https://github.com/vkcom/vk-miniapps-deploy", "dependencies": { - "chalk": "^4.1.2", - "configstore": "^5.0.1", - "fetch-blob": "^3.1.2", - "form-data-encoder": "^1.4.1", - "formdata-node": "^3.6.3", - "is-ci": "^3.0.0", - "node-fetch": "^2.6.1", - "prompts": "^2.4.1", - "terminal-link": "^2.1.1", - "zip-a-folder": "0.0.12" + "chalk": "~4.1.2", + "configstore": "~5.0.1", + "fetch-blob": "^3.1.4", + "form-data-encoder": "^1.7.1", + "formdata-node": "^4.3.2", + "is-ci": "^3.0.1", + "node-fetch": "^3.2.0", + "prompts": "^2.4.2", + "terminal-link": "~2.1.1", + "zip-a-folder": "~0.0.12" }, "devDependencies": { - "@types/node": "^16.4.13", - "@vkontakte/eslint-config": "^3.0.0", + "@types/node": "^17.0.16", + "@vkontakte/eslint-config": "^3.1.0", "eslint": "^7.32.0" } -} +} \ No newline at end of file From ee0c2b4413cee9a8a89d05fa8646d87cc327d9b5 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 8 Feb 2022 15:13:38 +0400 Subject: [PATCH 19/21] Fix node-fetch version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f09579..ab7b6e5 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "form-data-encoder": "^1.7.1", "formdata-node": "^4.3.2", "is-ci": "^3.0.1", - "node-fetch": "^3.2.0", + "node-fetch": "~2.6.7", "prompts": "^2.4.2", "terminal-link": "~2.1.1", "zip-a-folder": "~0.0.12" From a2d592b23804157bf951c14bb1750c9af825c522 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 8 Feb 2022 15:55:55 +0400 Subject: [PATCH 20/21] Fix upload --- lib/upload.js | 8 +++++++- package.json | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/upload.js b/lib/upload.js index 5c987c2..9c4e163 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -3,6 +3,7 @@ const fs = require('fs').promises; const access = require('fs').constants; const fetch = require('node-fetch').default; const stringify = require('querystring').stringify; +const glob = require('fast-glob'); const { zip } = require('zip-a-folder'); const { FormData } = require('formdata-node'); const { fileFromPath } = require('formdata-node/file-from-path'); @@ -44,7 +45,12 @@ module.exports = async (config) => { if (preBundlePath) { await fs.access(bundlePath, access.R_OK); } else { - const error = await zip(config.static_path, bundlePath, { compression: 0 }); + const prohibited = await glob(config.static_path + '/**/*.txt'); + + await Promise.all(prohibited.map((file) => fs.unlink(file))); + + const error = await zip(config.static_path, bundlePath); + if (error) { throw error; } diff --git a/package.json b/package.json index ab7b6e5..c061558 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "dependencies": { "chalk": "~4.1.2", "configstore": "~5.0.1", + "fast-glob": "^3.2.11", "fetch-blob": "^3.1.4", "form-data-encoder": "^1.7.1", "formdata-node": "^4.3.2", @@ -50,4 +51,4 @@ "@vkontakte/eslint-config": "^3.1.0", "eslint": "^7.32.0" } -} \ No newline at end of file +} From 042f3dc360b59d487e8f50038d304744b7de8ad3 Mon Sep 17 00:00:00 2001 From: eolme Date: Tue, 8 Feb 2022 16:18:02 +0400 Subject: [PATCH 21/21] Eliminate possibility of token leakage --- README.md | 26 ++++++++++++++++---------- lib/index.js | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index aee8b63..7f8ac41 100644 --- a/README.md +++ b/README.md @@ -56,17 +56,11 @@ To configure `vk-miniapps-deploy` all you need to do is specify a couple of thin and remove the suffix «.example» * Run yarn deploy -CI/CD is automatically detected, you only need to pass `access_token` to env or define in config: +CI/CD is automatically detected, you only need to pass `access_token` to env: ```bash $ cross-env MINI_APPS_ACCESS_TOKEN= yarn deploy ``` -or -```json -{ - "access_token": "" -} -``` with *user token* retrieved from vk-miniapps-deploy OR *service token* of deployable application @@ -74,12 +68,24 @@ There are two values to specify MINI_APPS_ENVIRONMENT: `production` or `dev`. All production builds will be also deployed on dev environment. If you grep URL paths, you can use environment variable `CI_URLS = true`. -URLs will be printed as followed structure: -```shell - +URLs will be printed as followed structure (⇆ is tab character): + +```md +# development +vk_app_desktop_dev_url:⇆url +vk_app_dev_url:⇆url +vk_mini_app_mvk_dev_url:⇆url + +# production +iframe_secure_url:⇆url +m_iframe_secure_url:⇆url +vk_mini_app_mvk_url:⇆url ``` +> See [URL_NAMES](./lib/deploy.js#L8) for more information. + If you always need to run in CI/CD-mode, in config: + ```json { "noprompt": true diff --git a/lib/index.js b/lib/index.js index c8dbe6c..ecbfc07 100644 --- a/lib/index.js +++ b/lib/index.js @@ -70,7 +70,7 @@ const DEFAULTS = { ENVIRONMENTS[process.env.MINI_APPS_ENVIRONMENT] || config.environment; // access_token - config.access_token = process.env.MINI_APPS_ACCESS_TOKEN || config.access_token || vault.get('access_token'); + config.access_token = process.env.MINI_APPS_ACCESS_TOKEN || vault.get('access_token'); if (!config.access_token) { console.log('Try to retrieve access token...'); config.access_token = await auth(config);