Skip to content

Commit 3ed20ad

Browse files
authored
Issue 21 (#28)
* WIP #21 Working pure JS solution for iOS * WIP #21 Add legacy option, remove prerequisites from README * WIP #21 Remove hardcoded xcodeproj filename * Rely on shebang inside cli.js instead of directly executing node binary * WIP #21 Add tests for checking version values * Refactor tests, add missing & legacy * Fix #21 Tweak paths, test names, enable Travis multi OS, update Node.js versions
1 parent b1c3a06 commit 3ed20ad

30 files changed

+1975
-1334
lines changed

.travis.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
os: osx
1+
os:
2+
- linux
3+
- osx
24
language: node_js
35
node_js:
4-
- "4"
5-
- "6"
6-
- "7"
6+
- "lts/argon"
7+
- "lts/boron"
8+
- "lts/carbon"
9+
- "node"

README.md

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,6 @@
22

33
Seamlessly shadows the behaviour of [`npm version`](https://docs.npmjs.com/cli/version).
44

5-
## Prerequisites
6-
7-
- Xcode Command Line Tools (`xcode-select --install`)
8-
9-
## Project setup
10-
11-
Open your Xcode project and under "Build Settings -> Versioning -> Current Project Version", set the value to your current `CFBundleVersion` ("General -> Identity -> Build").
12-
13-
---
14-
155
## npm-scripts hook (automatic method)
166

177
### Setup
@@ -72,6 +62,7 @@ react-native-version
7262
-b, --increment-build Only increment build number.
7363
-d, --android [path] Path to your "android/app/build.gradle" file.
7464
-i, --ios [path] Path to your "ios/" folder.
65+
-L, --legacy Version iOS using agvtool (macOS only). Requires Xcode Command Line Tools.
7566
-q, --quiet Be quiet, only report errors.
7667
-r, --reset-build Reset build number back to "1" (iOS only). Unlike Android's "versionCode", iOS doesn't require you to bump the "CFBundleVersion", as long as "CFBundleShortVersionString" changes. To make it consistent across platforms, react-native-version bumps both by default. You can use this option if you prefer to keep the build number value at "1" after every version change. If you then need to push another build under the same version, you can use "-bt ios" to increment.
7768
-t, --target <platforms> Only version specified platforms, eg. "--target android,ios".

cli.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ program
2929
defaults.android
3030
)
3131
.option("-i, --ios [path]", 'Path to your "ios/" folder.', defaults.ios)
32+
.option(
33+
"-L, --legacy",
34+
"Version iOS using agvtool (macOS only). Requires Xcode Command Line Tools."
35+
)
3236
.option("-q, --quiet", "Be quiet, only report errors.")
3337
.option(
3438
"-r, --reset-build",

index.js

Lines changed: 174 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
const beautify = require("js-beautify").html;
12
const child = require("child_process");
3+
const detectIndent = require("detect-indent");
4+
const flatten = require("lodash.flattendeep");
25
const fs = require("fs");
36
const list = require("./util").list;
47
const log = require("./util").log;
58
const path = require("path");
9+
const plist = require("plist");
610
const pSettle = require("p-settle");
11+
const stripIndents = require("common-tags/lib/stripIndents");
12+
const unique = require("lodash.uniq");
13+
const Xcode = require("pbxproj-dom/xcode").Xcode;
714

815
/**
916
* Custom type definition for Promises
@@ -27,6 +34,28 @@ function getDefaults() {
2734
};
2835
}
2936

37+
/**
38+
* Returns Info.plist filenames
39+
* @param {Xcode} xcode Opened Xcode project file
40+
* @return {Array} Plist filenames
41+
*/
42+
function getPlistFilenames(xcode) {
43+
return unique(
44+
flatten(
45+
xcode.document.projects.map(project => {
46+
return project.targets.map(target => {
47+
return target.buildConfigurationsList.buildConfigurations.map(
48+
config => {
49+
return config.ast.value.get("buildSettings").get("INFOPLIST_FILE")
50+
.text;
51+
}
52+
);
53+
});
54+
})
55+
)
56+
);
57+
}
58+
3059
/**
3160
* Versions your app
3261
* @param {Object} program commander/CLI-style options, camelCased
@@ -139,61 +168,157 @@ function version(program, projectPath) {
139168
ios = new Promise(function(resolve, reject) {
140169
log({ text: "Versioning iOS..." }, programOpts.quiet);
141170

142-
try {
143-
child.execSync("xcode-select --print-path", {
144-
stdio: ["ignore", "ignore", "pipe"]
145-
});
146-
} catch (err) {
147-
reject([
148-
{
149-
style: "red",
150-
text: err
151-
},
152-
{
153-
style: "yellow",
154-
text: "Looks like Xcode Command Line Tools aren't installed"
155-
},
156-
{
157-
text: "\n Install:\n\n $ xcode-select --install\n"
158-
}
159-
]);
171+
if (program.legacy) {
172+
try {
173+
child.execSync("xcode-select --print-path", {
174+
stdio: ["ignore", "ignore", "pipe"]
175+
});
176+
} catch (err) {
177+
reject([
178+
{
179+
style: "red",
180+
text: err
181+
},
182+
{
183+
style: "yellow",
184+
text: "Looks like Xcode Command Line Tools aren't installed"
185+
},
186+
{
187+
text: "\n Install:\n\n $ xcode-select --install\n"
188+
}
189+
]);
160190

161-
return;
162-
}
191+
return;
192+
}
163193

164-
const agvtoolOpts = {
165-
cwd: programOpts.ios
166-
};
194+
const agvtoolOpts = {
195+
cwd: programOpts.ios
196+
};
197+
198+
try {
199+
child.execSync("agvtool what-version", agvtoolOpts);
200+
} catch (err) {
201+
reject([
202+
{
203+
style: "red",
204+
text: "No project folder found at " + programOpts.ios
205+
},
206+
{
207+
style: "yellow",
208+
text: 'Use the "--ios" option to specify the path manually'
209+
}
210+
]);
167211

168-
try {
169-
child.execSync("agvtool what-version", agvtoolOpts);
170-
} catch (err) {
171-
reject([
172-
{
173-
style: "red",
174-
text: "No project folder found at " + programOpts.ios
175-
},
176-
{
177-
style: "yellow",
178-
text: 'Use the "--ios" option to specify the path manually'
179-
}
180-
]);
212+
return;
213+
}
181214

182-
return;
183-
}
215+
if (!programOpts.incrementBuild) {
216+
child.spawnSync(
217+
"agvtool",
218+
["new-marketing-version", appPkg.version],
219+
agvtoolOpts
220+
);
221+
}
184222

185-
if (!programOpts.incrementBuild) {
186-
child.spawnSync(
187-
"agvtool",
188-
["new-marketing-version", appPkg.version],
189-
agvtoolOpts
223+
if (programOpts.resetBuild) {
224+
child.execSync("agvtool new-version -all 1", agvtoolOpts);
225+
} else {
226+
child.execSync("agvtool next-version -all", agvtoolOpts);
227+
}
228+
} else {
229+
const xcode = Xcode.open(
230+
path.join(
231+
programOpts.ios,
232+
`${appPkg.name}.xcodeproj`,
233+
"project.pbxproj"
234+
)
190235
);
191-
}
192236

193-
if (programOpts.resetBuild) {
194-
child.execSync("agvtool new-version -all 1", agvtoolOpts);
195-
} else {
196-
child.execSync("agvtool next-version -all", agvtoolOpts);
237+
const plistFilenames = getPlistFilenames(xcode);
238+
239+
xcode.document.projects.forEach(project => {
240+
project.targets.forEach(target => {
241+
target.buildConfigurationsList.buildConfigurations.forEach(
242+
config => {
243+
if (target.name === appPkg.name) {
244+
config.patch({
245+
buildSettings: {
246+
CURRENT_PROJECT_VERSION: programOpts.resetBuild
247+
? 1
248+
: parseInt(
249+
config.ast.value
250+
.get("buildSettings")
251+
.get("CURRENT_PROJECT_VERSION").text,
252+
10
253+
) + 1
254+
}
255+
});
256+
}
257+
}
258+
);
259+
});
260+
261+
const plistFiles = plistFilenames.map(filename => {
262+
return fs.readFileSync(
263+
path.join(programOpts.ios, filename),
264+
"utf8"
265+
);
266+
});
267+
268+
const parsedPlistFiles = plistFiles.map(file => {
269+
return plist.parse(file);
270+
});
271+
272+
parsedPlistFiles.forEach((json, index) => {
273+
fs.writeFileSync(
274+
path.join(programOpts.ios, plistFilenames[index]),
275+
plist.build(
276+
Object.assign(
277+
{},
278+
json,
279+
!programOpts.incrementBuild
280+
? {
281+
CFBundleShortVersionString: appPkg.version
282+
}
283+
: {},
284+
{
285+
CFBundleVersion: `${programOpts.resetBuild
286+
? 1
287+
: parseInt(json.CFBundleVersion, 10) + 1}`
288+
}
289+
)
290+
)
291+
);
292+
});
293+
294+
plistFilenames.forEach((filename, index) => {
295+
const indent = detectIndent(plistFiles[index]);
296+
297+
fs.writeFileSync(
298+
path.join(programOpts.ios, filename),
299+
stripIndents`
300+
<?xml version="1.0" encoding="UTF-8"?>
301+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
302+
<plist version="1.0">` +
303+
"\n" +
304+
beautify(
305+
fs
306+
.readFileSync(path.join(programOpts.ios, filename), "utf8")
307+
.match(/<dict>[\s\S]*<\/dict>/)[0],
308+
{
309+
end_with_newline: true,
310+
indent_char: indent.indent,
311+
indent_size: indent.amount
312+
}
313+
) +
314+
stripIndents`
315+
</plist>` +
316+
"\n"
317+
);
318+
});
319+
});
320+
321+
xcode.save();
197322
}
198323

199324
log({ text: "iOS updated" }, programOpts.quiet);
@@ -299,5 +424,6 @@ function version(program, projectPath) {
299424

300425
module.exports = {
301426
getDefaults: getDefaults,
427+
getPlistFilenames: getPlistFilenames,
302428
version: version
303429
};

package.json

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,19 @@
1111
"dependencies": {
1212
"chalk": "^1.1.3",
1313
"commander": "^2.9.0",
14-
"p-settle": "^2.0.0"
14+
"common-tags": "^1.4.0",
15+
"detect-indent": "^5.0.0",
16+
"js-beautify": "^1.7.4",
17+
"lodash.flattendeep": "^4.4.0",
18+
"lodash.uniq": "^4.5.0",
19+
"p-settle": "^2.0.0",
20+
"pbxproj-dom": "^1.0.11",
21+
"plist": "^2.1.0"
1522
},
1623
"devDependencies": {
17-
"ava": "^0.17.0",
18-
"eslint": "^3.4.0",
19-
"eslint-plugin-ava": "^4.0.0",
24+
"ava": "^0.23.0",
25+
"eslint": "^4.10.0",
26+
"eslint-plugin-ava": "^4.2.2",
2027
"eslint-plugin-prettier": "^2.3.1",
2128
"fs-extra": "^1.0.0",
2229
"husky": "^0.14.3",
@@ -53,7 +60,7 @@
5360
"scripts": {
5461
"precommit": "lint-staged",
5562
"docs": "jsdoc2md -d 4 index.js",
56-
"test": "ava"
63+
"test": "./test.sh"
5764
},
5865
"lint-staged": {
5966
"*.{js,json}": ["prettier --write", "git add"]

test.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
3+
if [[ "$CI" != "true" || "$TRAVIS_OS_NAME" == "osx" ]]; then
4+
ava
5+
else
6+
ava --match="!*legacy*"
7+
fi

0 commit comments

Comments
 (0)