├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package.json └── src ├── action.js └── projectMetadata.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "jasmine": true 7 | }, 8 | "ecmaFeatures": { 9 | "arrowFunctions": true, 10 | "blockBindings": true, 11 | "classes": true, 12 | "defaultParams": true, 13 | "destructuring": true, 14 | "forOf": true, 15 | "generators": false, 16 | "modules": true, 17 | "objectLiteralComputedProperties": true, 18 | "objectLiteralDuplicateProperties": false, 19 | "objectLiteralShorthandMethods": true, 20 | "objectLiteralShorthandProperties": true, 21 | "spread": true, 22 | "superInFunctions": true, 23 | "templateStrings": true, 24 | "jsx": true 25 | }, 26 | "rules": { 27 | /** 28 | * Strict mode 29 | */ 30 | // babel inserts "use strict"; for us 31 | // http://eslint.org/docs/rules/strict 32 | "strict": [2, "never"], 33 | 34 | /** 35 | * ES6 36 | */ 37 | "no-var": 0, // http://eslint.org/docs/rules/no-var 38 | 39 | /** 40 | * Variables 41 | */ 42 | "no-shadow": 0, // http://eslint.org/docs/rules/no-shadow 43 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names 44 | "no-unused-vars": [0, { // http://eslint.org/docs/rules/no-unused-vars 45 | "vars": "local", 46 | "args": "after-used" 47 | }], 48 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define 49 | 50 | /** 51 | * Possible errors 52 | */ 53 | "comma-dangle": [2, "always-multiline"], // http://eslint.org/docs/rules/comma-dangle 54 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign 55 | "no-console": 0, // http://eslint.org/docs/rules/no-console 56 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger 57 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert 58 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition 59 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys 60 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case 61 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty 62 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign 63 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast 64 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi 65 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign 66 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations 67 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp 68 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace 69 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls 70 | "no-reserved-keys": 0, // http://eslint.org/docs/rules/no-reserved-keys 71 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays 72 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable 73 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan 74 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var 75 | 76 | /** 77 | * Best practices 78 | */ 79 | "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return 80 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly 81 | "default-case": 2, // http://eslint.org/docs/rules/default-case 82 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation 83 | "allowKeywords": true 84 | }], 85 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq 86 | "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in 87 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller 88 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return 89 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null 90 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval 91 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native 92 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind 93 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough 94 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal 95 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval 96 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks 97 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func 98 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str 99 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign 100 | "no-new": 2, // http://eslint.org/docs/rules/no-new 101 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func 102 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers 103 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal 104 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape 105 | "no-param-reassign": 1, // http://eslint.org/docs/rules/no-param-reassign 106 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto 107 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare 108 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign 109 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url 110 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare 111 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences 112 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal 113 | "no-with": 2, // http://eslint.org/docs/rules/no-with 114 | "radix": 2, // http://eslint.org/docs/rules/radix 115 | "vars-on-top": 0, // http://eslint.org/docs/rules/vars-on-top 116 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife 117 | "yoda": 2, // http://eslint.org/docs/rules/yoda 118 | 119 | /** 120 | * Style 121 | */ 122 | "indent": [2, 2], // http://eslint.org/docs/rules/ 123 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style 124 | "1tbs", { 125 | "allowSingleLine": true 126 | }], 127 | "quotes": [ 128 | 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes 129 | ], 130 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase 131 | "properties": "never" 132 | }], 133 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing 134 | "before": false, 135 | "after": true 136 | }], 137 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style 138 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last 139 | "func-names": 1, // http://eslint.org/docs/rules/func-names 140 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing 141 | "beforeColon": false, 142 | "afterColon": true 143 | }], 144 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap 145 | "newIsCap": true 146 | }], 147 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines 148 | "max": 2 149 | }], 150 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary 151 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object 152 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func 153 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces 154 | "no-extra-parens": 2, // http://eslint.org/docs/rules/no-wrap-func 155 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 156 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var 157 | "padded-blocks": 0, // http://eslint.org/docs/rules/padded-blocks 158 | "semi": [2, "always"], // http://eslint.org/docs/rules/semi 159 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing 160 | "before": false, 161 | "after": true 162 | }], 163 | "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords 164 | "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks 165 | "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren 166 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops 167 | "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case 168 | "spaced-comment": 2 // http://eslint.org/docs/rules/spaced-line-comment 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_* 3 | *.log 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alexey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Usage 3 | 4 | Ensure you have installed React Native Package Manager ([https://github.com/rnpm/rnpm](https://github.com/rnpm/rnpm)). 5 | 6 | ``` 7 | npm install rnpm-plugin-apphub 8 | 9 | # 0. commit 10 | git commit -am "Add new screen to statistics" 11 | 12 | # 1. upload 13 | rnpm apphub [appid] [token] 14 | 15 | ``` 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | func: require('./src/action'), 3 | description: 'Build, export and upload .ipa to AppHub Cloud', 4 | name: 'apphub [token] [appid]', 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rnpm-plugin-apphub", 3 | "version": "3.0.0", 4 | "description": "Build, export and upload .ipa to AppHub Cloud", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "eslint ./" 8 | }, 9 | "keywords": [ 10 | "rnpm", 11 | "react-native", 12 | "react-native apphub", 13 | "apphub" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/ptmt/rnpm-plugin-apphub.git" 18 | }, 19 | "engines": { 20 | "node": ">= 4.0.0" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/ptmt/rnpm-plugin-apphub/issues" 24 | }, 25 | "homepage": "https://github.com/ptmt/rnpm-plugin-apphub#readme", 26 | "license": "MIT", 27 | "dependencies": { 28 | "glob": "^6.0.1", 29 | "mkdirp": "^0.5.1", 30 | "npmlog": "^2.0.0", 31 | "plist": "^1.2.0", 32 | "uuid": "^3.0.0", 33 | "xcode": "^0.8.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/action.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const log = require('npmlog'); 3 | const https = require('https'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | const projectMetadata = require('./projectMetadata'); 7 | const glob = require('glob'); 8 | const childProcess = require('child_process'); 9 | const uuid = require('uuid'); 10 | const mkdirp = require('mkdirp'); 11 | 12 | const APPHUB_API = 'https://api.apphub.io/v1/upload'; 13 | 14 | log.heading = 'rnpm-apphub'; 15 | 16 | /** 17 | * Updates project and linkes all dependencies to it 18 | * 19 | * If optional argument [packageName] is provided, it's the only one that's checked 20 | */ 21 | module.exports = function link(config, args) { 22 | const project = config.getProjectConfig(); 23 | 24 | if (!project) { 25 | log.error('ERRPACKAGEJSON', `No package found. Are you sure it's a React Native project?`); 26 | return; 27 | } 28 | 29 | if (!project.ios) { 30 | log.error('ERRPACKAGEJSON', `No ios project found. Currently AppHub is only for iOS`); 31 | return; 32 | } 33 | 34 | /* 35 | * Build application .zip 36 | */ 37 | 38 | const plistFile = projectMetadata.getPlistPath(project) 39 | if (!plistFile) { 40 | log.error('ERRPLIST', '.plist is not found'); 41 | return; 42 | } 43 | const outputZip = path.join(process.cwd().replace(/ /g, '\\ '), './build/app.zip'); 44 | const tmpDir = path.join('/tmp', 'apphub', uuid.v4()); 45 | const buildDir = path.join(tmpDir, 'ios'); 46 | mkdirp.sync(buildDir); 47 | 48 | var options = [ 49 | '--entry-file', 'index.ios.js', 50 | '--dev', false, 51 | '--bundle-output', path.join(buildDir, 'main.jsbundle'), 52 | '--assets-dest', buildDir, 53 | '--platform', 'ios', 54 | ]; 55 | 56 | var cmds = [ 57 | 'node node_modules/react-native/local-cli/cli.js bundle ' + options.join(' '), 58 | 'cp ' + plistFile + ' ' + buildDir, 59 | 'cd ' + tmpDir + ' && zip -r ' + outputZip + ' ios', 60 | ]; 61 | for (var i = 0; i < cmds.length; i++) { 62 | var cmd = cmds[i]; 63 | childProcess.execSync(cmd, { stdio: [0, 1, 2] }, { cwd: process.cwd()}); 64 | } 65 | 66 | /* 67 | * Filling the metadata from plist and last git commit 68 | */ 69 | const lastGitMessage = childProcess.execSync('git log -1 --pretty=%B | cat').toString().split('\n')[0]; 70 | const metadata = projectMetadata.getProjectMetadata(project); 71 | if (!metadata) { 72 | log.error('ERRPLIST', '.plist is not found'); 73 | return; 74 | } 75 | /* 76 | * Put file onto the server 77 | */ 78 | if (args.length < 2) { 79 | log.error('ERRTOKENS', 'No AppID and AppTokens provided. Please obtain them from AppHub dashbaord'); 80 | return; 81 | } 82 | 83 | log.info('Uploading build...', metadata.version, lastGitMessage); 84 | 85 | const result = childProcess.execSync(` 86 | curl -X PUT \ 87 | -H "X-AppHub-Application-ID: ${args[0]}" \ 88 | -H "X-AppHub-Application-Secret: ${args[1]}" \ 89 | -H "Content-Type: application/zip" \ 90 | -H 'X-AppHub-Build-Metadata: { 91 | "target": "all", 92 | "name": "${metadata.version}", 93 | "description": "${lastGitMessage} // by rnpm-plugin-apphub", 94 | "app_versions": ["${metadata.shortVersion}"] 95 | }' \ 96 | -L https://api.apphub.io/v1/upload \ 97 | --upload-file ./build/app.zip 98 | `); 99 | 100 | log.info(result); 101 | 102 | }; 103 | -------------------------------------------------------------------------------- /src/projectMetadata.js: -------------------------------------------------------------------------------- 1 | const plistParser = require('plist'); 2 | const glob = require('glob'); 3 | const xcode = require('xcode'); 4 | const path = require('path'); 5 | const fs = require('fs'); 6 | 7 | module.exports = {}; 8 | 9 | module.exports.getPlistPath = function(projectConfig) { 10 | const project = xcode.project(projectConfig.ios.pbxprojPath).parseSync(); 11 | 12 | const plistPath = path.join( 13 | projectConfig.ios.sourceDir, 14 | project.getBuildProperty('INFOPLIST_FILE').replace(/"/g, '').replace('$(SRCROOT)', '') 15 | ); 16 | 17 | if (!fs.existsSync(plistPath)) { 18 | return false; 19 | } 20 | 21 | return plistPath; 22 | }; 23 | 24 | module.exports.getProjectMetadata = function(projectConfig) { 25 | const plistPath = module.exports.getPlistPath(projectConfig); 26 | 27 | if (!plistPath) { 28 | return false; 29 | } 30 | 31 | const plist = plistParser.parse( 32 | fs.readFileSync(plistPath, 'utf-8') 33 | ); 34 | 35 | return { 36 | version: plist.CFBundleVersion, 37 | shortVersion: plist.CFBundleShortVersionString, 38 | bundleName: plist.CFBundleName, 39 | }; 40 | }; 41 | --------------------------------------------------------------------------------