Skip to content

Fix xcode9 provision, provide --quiet by default, enable -allowProvisioningUpdates - #3070

Merged
PanayotCankov merged 6 commits into
masterfrom
fix-xcode9-provision
Aug 23, 2017
Merged

PanayotCankov merged 6 commits into
masterfrom
fix-xcode9-provision

Conversation

@PanayotCankov

@PanayotCankov PanayotCankov commented Aug 17, 2017

Copy link
Copy Markdown
Contributor

Depends on:
#3069

This PR will:

  • Update the export options plist and list the provisioning profile so Xcode9 can export when working with manual signing style and the --provision switch.
  • Provide --quiet to xcodebuild and -quiet to the gradle tools so they log only errors by default. The command line args with which these commands are called will be printed in place of the verbose output. You can switch to --log trace for full output.
  • Provide -allowProvisioningUpdates to xcodebuild when working with Xcode version > 9.0 so when passing team, xcodebuild may generate automatically managed provisioning profiles for you
    (THIS ONE IS MY FAVOURITE!!!)
  • --teamId now behaves like --provision, it will require prepare if the last time other teamId was issued, it will print all teams if no arg is provided, it will switch to automatic signing
  • using --teamId and --provision will throw that they are mutually exclusive
  • teamId should work with team name, if we find a provision that has the team name and id
    (we should improve that by somehow looking in the accounts registered in Xcode)

…iet flags to xcodebuild and gradle, provide -allowProvisioningUpdates to xcodebuild > 9.0
…re mutually exclusive, teamId now should work with name if a provision that has the team id for the provided team name is found
dtopuzov added a commit to NativeScript/nativescript-cli-tests that referenced this pull request Aug 21, 2017
- Make tests passing with
NativeScript/nativescript-cli#3070
- Enable —provision tests for Xcode9
const gradlew = this.$hostInfo.isWindows ? "gradlew.bat" : "./gradlew";

const localArgs = [...gradleArgs];
if (this.$logger.getLevel() === "INFO") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use $loggingLevels.info here instead of a string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's keep it this way, $loggingLevels has a different purpose. I'll handle the logging levels in a separate PR and will change this code with a constant in it.

return { devices, match };
}

public async getDevelopmentTeams(): Promise<{ id: string, name: string }[]> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{ id: string, name: string } could be extracted in an interface.
This is a personal preference though as I find named interfaces improve code readability

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used once, if I have to write it out in 2-3 places perhaps I will make it a separate interface. For now type inference works so well, this type is never written out anywhere else in the code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a scenario where we'll need interface, but let's discuss it personally and handle it in a separate PR once it is needed.

}

public async getDevelopmentTeams(): Promise<{ id: string, name: string }[]> {
const teams: { [teamName: string]: Set<string> } = {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same remark about the interface

@PanayotCankov PanayotCankov Aug 23, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used locally and just once. I don't want to pollute the module with interfaces. It could be IDictionary<Set<string>> but then it would be even harder to figure out what the key would be.

if (teamId === true) {
await this.$iOSProvisionService.listTeams();
this.$errors.failWithoutHelp("Please provide team id or team name with the --teamId options.");
return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need for this return statement - this.$errors.failWithoutHelp will terminate the process

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Omg. It returns void. :D I will make it "never".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

failWithoutHelp(message: string, ...args: any[]): never;
please use the never type for each method that terminate unconditionally the process or throw.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a PR for 'never' but it is completely unrelated (I think) with this current PR:
telerik/mobile-cli-lib#1006

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one! Yes, it is unrelated

Comment thread lib/services/ios-project-service.ts Outdated
args.push(`CODE_SIGN_IDENTITY=${buildConfig.codeSignIdentity}`);
}
// These are now set in the .pbxproj, avoid passing them as arguments!
// if (buildConfig && buildConfig.codeSignIdentity) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to keep this code commented out for future reference?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vote for deleting it

Comment thread lib/services/ios-project-service.ts Outdated

if (provision !== undefined) {
if (signing && signing.style === "Manual") {
for (let name in signing.configurations) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let name could be const name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a lint rule for these?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, but enabling it leads to 1400 compile errors, so I advise against this in this PR

Comment thread lib/services/ios-project-service.ts Outdated
if (provision !== undefined) {
if (signing && signing.style === "Manual") {
for (let name in signing.configurations) {
let config = signing.configurations[name];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let could be const

<string>${options.teamID}</string>
`;
}
if (options && options.provision) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A separate service/helper function could be extracted for getting the exportOptions.plist file and be reused here and here.
This could be done in another PR though

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The time is tight, let's do this in a separate PR if we ever touch these two again.

Comment thread lib/services/ios-project-service.ts Outdated
}

await this.createIpa(projectRoot, projectData, buildConfig);
private async setupSigningFromTeam(projectRoot: string, projectData: IProjectData, teamId?: string) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the teamId argument really optional here - what will happen if it is not passed - shouldUpdateXcode would be set to true and then Automatic signing will be set without a team.

let shouldUpdateXcode = false;
if (signing && signing.style === "Automatic") {
if (signing.team !== teamId) {
// Maybe the provided team is name such as "Telerik AD" and we need to convert it to CH******37

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the team is passed as an id instead of a name - we'd still try and look for it as if it was a name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I think it is highly unlikely to have a team name matching the randomly generated team ids on the same machine. teamId probably should be just team like the provision switch is not provisionIdentifier or provisionUUID. Being able to pass "Telerik AD" for team however is really convenient.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, I didn't manage to express my concern correctly - according to the docs you've written for --team-id one can either pass a team name or a team id.
So what of the case where the user knows the team id and passes it as a value to --team-id - we'd still look for it and validate it as if it was a team name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code here will do the following:
It will check if the arg passed to team-id matches the team in the Automatic signing style in Xcode. If it does, the native project and prepare are up-to-date.
If it does not - it will consider the team-id to be actually a team name, it will check for a team with name matching the provided team-id and if it finds one it will check if Xcode is configured to sign with that team's id.

This can fail if you have a team that has team id matching one of your other team's names, that's highly unlikely. If this raises as an issue we can split a team-id and team-name switches.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pretty much try to treat team-id as id, and eventually check the provisioning profiles if it were team name and map the name to id.

const gradlew = this.$hostInfo.isWindows ? "gradlew.bat" : "./gradlew";

const localArgs = [...gradleArgs];
if (this.$logger.getLevel() === "INFO") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's keep it this way, $loggingLevels has a different purpose. I'll handle the logging levels in a separate PR and will change this code with a constant in it.

Comment thread lib/services/android-project-service.ts Outdated
const localArgs = [...gradleArgs];
if (this.$logger.getLevel() === "INFO") {
localArgs.push("--quiet");
this.$logger.info(`${gradlew} ${localArgs.join(" ")}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we want to print the command that we execute in the info level?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise nothing is printed. I wanted to print something that indicates that a native build had started. I am ok to go for just "Xcode build..." and "Gradle build...", should I change these?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer using Gradle build...
In case you want to print dots, you can use:

const action = () => this.spawn(gradlew, localArgs, childProcessOpts, spawnFromEventOptions);
this.$logger.printInfoMessageOnSameLine("Gradle build...");
await this.$progressIndicator.showProgressIndicator(action(), 2000);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this throw garbage progress indicator symbols around if the xcodebuild or gradle build actually output stuff? I've seen rogue npm progress bars printed at random.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually sounds reasonable I'll give it a try.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok we've agreed to use "Gradle build..." and "Xcode build...".
Bear in mind that we do 3 xcodebuild executions on clean "tns run ios" on device.

<string>${exportOptionsMethod}</string>`;
if (options && options.provision) {
plistTemplate += ` <key>provisioningProfiles</key>
<dict>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this plist seems duplicate of the one used on line 234, can we have it on a single place and reuse it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First Write, Second Copy, Third Refactor

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed to handle this in a separate PR

Comment thread lib/services/ios-project-service.ts Outdated
args.push(`CODE_SIGN_IDENTITY=${buildConfig.codeSignIdentity}`);
}
// These are now set in the .pbxproj, avoid passing them as arguments!
// if (buildConfig && buildConfig.codeSignIdentity) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vote for deleting it

Comment thread lib/services/ios-project-service.ts Outdated
localArgs.push("-allowProvisioningUpdates");
}
} catch (e) {
console.warn("Failed to use xcodebuild with -allowProvisioningUpdates due to error: " + e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be $logger.warn

} catch (e) {
console.warn("Failed to use xcodebuild with -allowProvisioningUpdates due to error: " + e);
}
if (this.$logger.getLevel() === "INFO") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above:
let's keep it this way, $loggingLevels has a different purpose. I'll handle the logging levels in a separate PR and will change this code with a constant in it.

Comment thread lib/services/ios-project-service.ts Outdated
}
if (this.$logger.getLevel() === "INFO") {
localArgs.push("-quiet");
this.$logger.info(`xcodebuild ${localArgs.join(" ")}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and again, is it important for the user to see the command we are executing. Until now this was not shown, can we show it in trace level only?

ghost Aug 23, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same,

xcodebuild will not output a single line if run with -quiet and no errors or warning were present. So far the user had seen 10 screens of output in place of this with all the flags and environment variables passed to Xcode, various subtools that compile storyboards or copy PNGs. All of it is now collapsed to this line. I think this line is important otherwise the CLI will stall for several seconds and the user won't know that the native build actually started.

Again I can change the message if you provide me a better one, but I think it is important to appear in the default logging mode when -quiet is passed.

ghost Aug 23, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

In case you want to print dots, you can use:

const action = () => this.$childProcess.spawnFromEvent("xcodebuild", ... );
this.$logger.printInfoMessageOnSameLine("Building application...");
await this.$progressIndicator.showProgressIndicator(action(), 2000);

Comment thread lib/services/ios-provision-service.ts Outdated
* Formats the argument so it can easily be copied from the terminal and provided as value for an option.
* @param arg The string to format.
*/
function cmdEscape(arg: string): string {

ghost Aug 23, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe quoteString can do the same work for you?

ghost Aug 23, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. It does cmdEscape. Its purpose is to make the string passable as argument to an option. Telerik A D can be escaped as "Telerik A D" or it could be as Telerik\ A\ D and have no commas at all and I wouldn't care less which one would be the result of the escape. On the other hand the escaping is flawed, it would fail on Telerik "AD" as the commas will go nuts with "Telerik "AD"" :( but I don't have time to figure out better escaping.

ghost Aug 23, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omg :D I clicked the link, you have cmdQuote :D I will use that one :)

@NativeScript NativeScript deleted a comment from dtopuzov Aug 23, 2017
@NativeScript NativeScript deleted a comment from dtopuzov Aug 23, 2017
@PanayotCankov

ghost commented Aug 23, 2017

Copy link
Copy Markdown
Contributor Author

@rosen-vladimirov @Mitko-Kerezov
I've addressed most of the important comments in a separate commit.
Could you take a look again?

ghost left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@PanayotCankov
PanayotCankov merged commit 9fb0efc into master Aug 23, 2017
@PanayotCankov
PanayotCankov deleted the fix-xcode9-provision branch August 23, 2017 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants