Skip to content

Merge post_install hooks in Podfile #2256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,12 @@ interface ICocoaPodsService {
* @return {string} The footer which needs to be placed at the end of a Podfile.
*/
getPodfileFooter(): string;

/**
* Merges the content of hooks with the provided name if there are more than one hooks with this name in the Podfile.
* @param {string} hookName The name of the hook.
* @param {string} pathToPodfile The path to the Podfile.
* @return {IFuture<void>}
*/
mergePodfileHookContent(sectionName: string, pathToPodfile: string): IFuture<void>
}
57 changes: 57 additions & 0 deletions lib/services/cocoapods-service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,70 @@
import {EOL} from "os";

interface IRubyFunction {
functionName: string;
functionParameters?: string;
}

export class CocoaPodsService implements ICocoaPodsService {
constructor(private $fs: IFileSystem) { }

public getPodfileHeader(targetName: string): string {
return `use_frameworks!${EOL}${EOL}target "${targetName}" do${EOL}`;
}

public getPodfileFooter(): string {
return `${EOL}end`;
}

public mergePodfileHookContent(hookName: string, pathToPodfile: string): IFuture<void> {
return (() => {
if (!this.$fs.exists(pathToPodfile).wait()) {
throw new Error(`The Podfile ${pathToPodfile} does not exist.`);
}

let podfileContent = this.$fs.readText(pathToPodfile).wait();
let hookStart = `${hookName} do`;

let hookDefinitionRegExp = new RegExp(`${hookStart} *(\\|(\\w+)\\|)?`, "g");
let newFunctionNameIndex = 1;
let newFunctions: IRubyFunction[] = [];

let replacedContent = podfileContent.replace(hookDefinitionRegExp, (substring: string, firstGroup: string, secondGroup: string, index: number): string => {
let newFunctionName = `${hookName}${newFunctionNameIndex++}`;
let newDefinition = `def ${newFunctionName}`;

let rubyFunction: IRubyFunction = { functionName: newFunctionName };
// firstGroup is the block parameter, secondGroup is the block parameter name.
if (firstGroup && secondGroup) {
newDefinition = `${newDefinition} (${secondGroup})`;
rubyFunction.functionParameters = secondGroup;
}

newFunctions.push(rubyFunction);
return newDefinition;
});

if (newFunctions.length > 1) {
// Execute all methods in the hook and pass the parameter to them.
let blokParameterName = "installer";
let mergedHookContent = `${hookStart} |${blokParameterName}|${EOL}`;

_.each(newFunctions, (rubyFunction: IRubyFunction) => {
let functionExecution = rubyFunction.functionName;
if (rubyFunction.functionParameters && rubyFunction.functionParameters.length) {
functionExecution = `${functionExecution} ${blokParameterName}`;
}

mergedHookContent = `${mergedHookContent} ${functionExecution}${EOL}`;
});

mergedHookContent = `${mergedHookContent}end`;

let newPodfileContent = `${replacedContent}${EOL}${mergedHookContent}`;
this.$fs.writeFile(pathToPodfile, newPodfileContent).wait();
}
}).future<void>()();
}
}

$injector.register("cocoapodsService", CocoaPodsService);
26 changes: 13 additions & 13 deletions lib/services/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,10 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
args = args.concat((buildConfig && buildConfig.architectures) || defaultArchitectures);

let xcodeBuildVersion = this.getXcodeVersion();
if (helpers.versionCompare(xcodeBuildVersion, "8.0")>=0) {
if (helpers.versionCompare(xcodeBuildVersion, "8.0") >= 0) {
let teamId = this.getDevelopmentTeam();
if (teamId) {
args = args.concat("DEVELOPMENT_TEAM="+teamId);
args = args.concat("DEVELOPMENT_TEAM=" + teamId);
}
}
} else {
Expand Down Expand Up @@ -478,10 +478,10 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
try {
this.$fs.createDirectory(path.dirname(compatabilityXibPath)).wait();
this.$fs.writeFile(compatabilityXibPath, content).wait();
} catch(e) {
} catch (e) {
this.$logger.warn("We have failed to add compatability LaunchScreen.xib due to: " + e);
}
} catch(e) {
} catch (e) {
this.$logger.warn("We have failed to check if we need to add a compatability LaunchScreen.xib due to: " + e);
}
}
Expand Down Expand Up @@ -687,7 +687,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f

let firstPostInstallIndex = projectPodfileContent.indexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME);
if (firstPostInstallIndex !== -1 && firstPostInstallIndex !== projectPodfileContent.lastIndexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME)) {
this.$logger.warn(`Podfile contains more than one post_install sections. You need to open ${this.projectPodFilePath} file and manually resolve this issue.`);
this.$cocoapodsService.mergePodfileHookContent(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME, this.projectPodFilePath).wait();
}

let xcuserDataPath = path.join(this.xcodeprojPath, "xcuserdata");
Expand Down Expand Up @@ -948,7 +948,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
let pluginPlatformsFolderPath = plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
let pluginXcconfigFilePath = path.join(pluginPlatformsFolderPath, "build.xcconfig");
if (this.$fs.exists(pluginXcconfigFilePath).wait()) {
this.mergeXcconfigFiles(pluginXcconfigFilePath,this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath).wait();
this.mergeXcconfigFiles(pluginXcconfigFilePath, this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath).wait();
}
}

Expand Down Expand Up @@ -999,7 +999,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
return xcodeBuildVersion;
}

private getDevelopmentTeams(): Array<{id:string, name: string}> {
private getDevelopmentTeams(): Array<{ id: string, name: string }> {
let dir = path.join(process.env.HOME, "Library/MobileDevice/Provisioning Profiles/");
let files = this.$fs.readDirectory(dir).wait();
let teamIds: any = {};
Expand All @@ -1012,18 +1012,18 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
teamIds[teamId] = teamName;
}
}
let teamIdsArray = new Array<{id:string, name: string}>();
let teamIdsArray = new Array<{ id: string, name: string }>();
for (let teamId in teamIds) {
teamIdsArray.push({ id:teamId, name:teamIds[teamId] });
teamIdsArray.push({ id: teamId, name: teamIds[teamId] });
}
return teamIdsArray;
}

private getProvisioningProfileValue(name: string, text: string): string {
let findStr = "<key>"+name+"</key>";
let findStr = "<key>" + name + "</key>";
let index = text.indexOf(findStr);
if (index > 0) {
index = text.indexOf("<string>", index+findStr.length);
index = text.indexOf("<string>", index + findStr.length);
if (index > 0) {
index += "<string>".length;
let endIndex = text.indexOf("</string>", index);
Expand All @@ -1043,7 +1043,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
line = line.replace(/\/(\/)[^\n]*$/, "");
if (line.indexOf("DEVELOPMENT_TEAM") >= 0) {
teamId = line.split("=")[1].trim();
if (teamId[teamId.length-1] === ';') {
if (teamId[teamId.length - 1] === ';') {
teamId = teamId.slice(0, -1);
}
}
Expand Down Expand Up @@ -1086,7 +1086,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
"Yes, persist the team id in platforms folder.",
"No, don't persist this setting."
];
let choicePersist = this.$prompter.promptForChoice("Do you want to make teamId: "+teamId+" a persistent choice for your app?", choicesPersist).wait();
let choicePersist = this.$prompter.promptForChoice("Do you want to make teamId: " + teamId + " a persistent choice for your app?", choicesPersist).wait();
switch (choicesPersist.indexOf(choicePersist)) {
case 0:
let xcconfigFile = path.join(this.$projectData.appResourcesDirectoryPath, this.platformData.normalizedPlatformName, "build.xcconfig");
Expand Down
178 changes: 178 additions & 0 deletions test/cocoapods-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import * as yok from "../lib/common/yok";
import {assert} from "chai";
import {CocoaPodsService} from "../lib/services/cocoapods-service";
import {EOL} from "os";
import Future = require("fibers/future");

interface IMergePodfileHooksTestCase {
input: string;
output: string;
testCaseDescription: string;
}

function createTestInjector(): IInjector {
let testInjector: IInjector = new yok.Yok();

testInjector.register("fs", {});
testInjector.register("cocoapodsService", CocoaPodsService);

return testInjector;
}

// The newline characters should be replaced with EOL because on Windows the EOL is \r\n
// but the character which is placed in `` for newline is only \n
// if we do not replace the newline characters the tests will pass only on linux and mac.
function changeNewLineCharacter(input: string): string {
return input ? input.replace(/\r?\n/g, EOL) : input;
}

describe("Cocoapods service", () => {
describe("merge Podfile hooks", () => {
let testInjector: IInjector;
let cocoapodsService: ICocoaPodsService;
let newPodfileContent: string;

let mockFileSystem = (injector: IInjector, podfileContent: string): void => {
let fs: IFileSystem = injector.resolve("fs");

fs.exists = () => Future.fromResult(true);
fs.readText = () => Future.fromResult(podfileContent);
fs.writeFile = (pathToFile: string, content: any) => {
newPodfileContent = content;
return Future.fromResult();
};
};

let testCaces: IMergePodfileHooksTestCase[] = [
{
input: `
target 'MyApp' do
pod 'GoogleAnalytics', '~> 3.1'
target 'MyAppTests' do
inherit! :search_paths
pod 'OCMock', '~> 2.0.1'
end
end

post_install do |installer|
installer.pods_project.targets.each do |target|
puts target.name
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
puts target.name
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
puts target.name
end
end`,
output: `
target 'MyApp' do
pod 'GoogleAnalytics', '~> 3.1'
target 'MyAppTests' do
inherit! :search_paths
pod 'OCMock', '~> 2.0.1'
end
end

def post_install1 (installer)
installer.pods_project.targets.each do |target|
puts target.name
end
end
def post_install2 (installer)
installer.pods_project.targets.each do |target|
puts target.name
end
end
def post_install3 (installer)
installer.pods_project.targets.each do |target|
puts target.name
end
end
post_install do |installer|
post_install1 installer
post_install2 installer
post_install3 installer
end`,
testCaseDescription: "should merge more than one hooks with block parameter correctly."
}, {
input: `
target 'MyApp' do
pod 'GoogleAnalytics', '~> 3.1'
target 'MyAppTests' do
inherit! :search_paths
pod 'OCMock', '~> 2.0.1'
end

post_install do |installer_representation|
installer_representation.pods_project.targets.each do |target|
puts target.name
end
end
post_install do
puts "Hello World!"
end
end`,
output: `
target 'MyApp' do
pod 'GoogleAnalytics', '~> 3.1'
target 'MyAppTests' do
inherit! :search_paths
pod 'OCMock', '~> 2.0.1'
end

def post_install1 (installer_representation)
installer_representation.pods_project.targets.each do |target|
puts target.name
end
end
def post_install2
puts "Hello World!"
end
end
post_install do |installer|
post_install1 installer
post_install2
end`,
testCaseDescription: "should merge more than one hooks with and without block parameter correctly."
}, {
input: `
target 'MyApp' do
pod 'GoogleAnalytics', '~> 3.1'
target 'MyAppTests' do
inherit! :search_paths
pod 'OCMock', '~> 2.0.1'
end
end

post_install do |installer|
installer.pods_project.targets.each do |target|
puts target.name
end
end`,
output: null,
testCaseDescription: "should not change the Podfile when there is only one hook."
}
];

beforeEach(() => {
testInjector = createTestInjector();
cocoapodsService = testInjector.resolve("cocoapodsService");
newPodfileContent = null;
});

_.each(testCaces, (testCase: IMergePodfileHooksTestCase) => {
it(testCase.testCaseDescription, () => {
mockFileSystem(testInjector, testCase.input);

cocoapodsService.mergePodfileHookContent("post_install", "").wait();

assert.deepEqual(changeNewLineCharacter(newPodfileContent), changeNewLineCharacter(testCase.output));
});
});
});
});
Loading