Using Jamf to deploy Xcode 13.2.1 for MacOS 12.3 Monterey

lynnaj
New Contributor III

Since the most recent versions of Xcode are often a bit "buggy", the faculty that teach in our Computer Science labs prefer to run a slightly older version of Xcode. This adds some challenges to deploying Xcode as the APP store route using VPP can only install the most recent version of an app.

In case anyone else has the same need, here is the method that I've come up with. This is working for me using Xcode 13.2.1 on MacOS 12.3 Monterey and Jamf Pro version 10.37.2.

I hope this helps others navigate this challenge ...

 

1) Setup a "test" mac with similar hardware and the same MacOS that is DEP enrolled in Jamf just like your target macs. (You probably already have this as it's incredibly useful for so many things!)

2) Get a Apple Developer's account  (I believe a free account will work)

3) Download the version of Xcode  you want. (Only certain versions of Xcode will work on certain MacOSs so get the right version!)

4) Xcode is delivered in an Apple specific package format which is no longer compatible directly with Jamf. Double click that downloaded package to extract the Xcode application.

5) Move the extracted application to the /Applications folder on the test mac you setup in step #1 above.

6) Use Packages from WhiteBox to create a "raw" package with just Xcode in /Applicaations (Before running, make sure Packages has "Full Disk Access" in Security and Privacy System Preferences.  Also note that I was completely unsuccessful in multiple attempts of packaging this massive application with Jamf Composer! Xcode as it sits in /Applications is about 36 GBs and the resulting, compressed package is over 16 GBs ...  )

7) Copy that Packages created Xcode package to your Jamf Pro package repository using Jamf Admin.

8) Now on your Jamf Pro server create a policy as follows:

Scope the policy to a smart group to target the macs you want this to install on.  The smart group criteria I used were:  Application Title does not have Xcode AND Operating System like 12.3   (you may want to add a few more criteria since you don't want to run this on many macs at the same time. The Xcode compressed package is 16 GBs!)

Set the policy to execute on recurring check-in and once per computer. I added a client-side limitation set to Do not run between 8:00 am and 11:45 pm since trying to do this when the network is busy during the day is, I think, asking for trouble!

Include the Xcode package that you uploaded with Jamf Admin above

Include two scripts - (both below)

One script to set the sleep timers to 6 hours which is set to execute "Before" anything else in the policy runs  (You don't want the macs going to sleep when they are running this policy!)

The second script is the Xcode configuration script and is set to execute "'After"  (This script also does a Jamf inventory at the end to drop the targeted mac out of the smart group and sets the sleep timers back to something more normal like 45 minutes)

9) Find Xcode on the target macs the next morning ready for use by standard users!

 

Set Sleep Timers to 6 hours Script:

#!/bin/sh

# Set sleep timers so the macs don't go to sleep while installing software!
pmset -a sleep 360
pmset -a disksleep 360
pmset -a displaysleep 360

exit 0

 

 

 

Xcode Configuration Script: (When/If you copy this script watch for unintended line breaks!)

#!/bin/zsh

# Lots of this code came from - https://www.jamf.com/jamf-nation/discussions/26615/xcode-9-2-deployment
# I added logging so we can see what happened after runnning this
# Also converted from bash to zsh, mostly changed if statements
# Also added CodeTypes.pkg install for Xcode version 13.2.1
# Lynna Jackson, April 2022

# Create log file 
mkdir -p /Library/Logs/WilliamsCS/
touch /Library/Logs/WilliamsCS/XCodeInstall.log
# close standard output
exec 1<&-
#close error output
exec 2<&-
#open log file above for read and write
exec 1<>/Library/Logs/WilliamsCS/XCodeInstall.log
# Redirect all output to log file above
exec 2>&1

echo "Script started: Xcode Configure at  $(date)"
echo " ------- "

# Accept EULA so there is no prompt

if [[ -e "/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild" ]] 
then
  echo " ------- "  
  echo "First license accepted at:  $(date)"
  "/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild" -license accept  
fi

# Just in case the xcodebuild command above fails to accept the EULA, set the license acceptance info 
# in /Library/Preferences/com.apple.dt.Xcode.plist. For more details on this, see Tim Sutton's post: 
# http://macops.ca/deploying-xcode-the-trick-with-accepting-license-agreements/

if [[ -e "/Applications/Xcode.app/Contents/Resources/LicenseInfo.plist" ]] 
then
   xcode_version_number=`/usr/bin/defaults read "/Applications/Xcode.app/Contents/"Info CFBundleShortVersionString`
   xcode_build_number=`/usr/bin/defaults read "/Applications/Xcode.app/Contents/Resources/"LicenseInfo licenseID`
   xcode_license_type=`/usr/bin/defaults read "/Applications/Xcode.app/Contents/Resources/"LicenseInfo licenseType`
   echo " ------- "
   echo "Second license accepted at:  $(date)"
   echo "$xcode_version_number"
   echo "$xcode_build_number"
   echo "$xcode_license_type"   
   
   if [[ "${xcode_license_type}" == "GM" ]] 
   then
       /usr/bin/defaults write "/Library/Preferences/"com.apple.dt.Xcode IDEXcodeVersionForAgreedToGMLicense "$xcode_version_number"
       /usr/bin/defaults write "/Library/Preferences/"com.apple.dt.Xcode IDELastGMLicenseAgreedTo "$xcode_build_number"
    else
       /usr/bin/defaults write "/Library/Preferences/"com.apple.dt.Xcode IDEXcodeVersionForAgreedToBetaLicense "$xcode_version_number"
       /usr/bin/defaults write "/Library/Preferences/"com.apple.dt.Xcode IDELastBetaLicenseAgreedTo "$xcode_build_number"
   fi       
fi

# DevToolsSecurity tool to change the authorization policies, such that a user who is a
# member of either the admin group or the _developer group does not need to enter an additional
# password to use the Apple-code-signed debugger or performance analysis tools.
echo " ------- "
echo "DevToolsSecurity enabled at:  $(date)"
/usr/sbin/DevToolsSecurity -enable

# Add all users to developer group, if they're not admins
echo " ------- "
echo "All Users add to developer group at:  $(date)"
/usr/sbin/dseditgroup -o edit -a everyone -t group _developer


# If you have multiple versions of Xcode installed, specify which one you want to be current.
echo " ------- "
echo "Setting /Applications/Xcode as current Xcode at:  $(date)"
/usr/bin/xcode-select --switch /Applications/Xcode.app

# Bypass Gatekeeper verification for Xcode, which can take hours.
if [[ -e "/Applications/Xcode.app" ]]; then 
  echo " ------- "
  echo "Remove Gatekeeper's quaratine bit at:  $(date)"
  xattr -dr com.apple.quarantine /Applications/Xcode.app
fi

# Install Mobile Device Packages so there are no prompts
if [[ -e "/Applications/Xcode.app/Contents/Resources/Packages/MobileDevice.pkg" ]]
then
  echo " ------- "  
  echo "Installing Mobile Device Package at:  $(date)"
  /usr/sbin/installer -dumplog -verbose -pkg "/Applications/Xcode.app/Contents/Resources/Packages/MobileDevice.pkg" -target /
fi

if [[ -e "/Applications/Xcode.app/Contents/Resources/Packages/MobileDeviceDevelopment.pkg" ]]
then
  echo " ------- "    
  echo "Installing Mobile Device Deployment Package at:  $(date)"
  /usr/sbin/installer -dumplog -verbose -pkg "/Applications/Xcode.app/Contents/Resources/Packages/MobileDeviceDevelopment.pkg" -target /
fi

# Install XcodeExtensionSupport.pkg
if [[ -e "/Applications/Xcode.app/Contents/Resources/Packages/XcodeExtensionSupport.pkg" ]]
then
  echo " ------- "  
  echo "Installing XCode Extensions Support package at:  $(date)"
  /usr/sbin/installer -dumplog -verbose -pkg "/Applications/Xcode.app/Contents/Resources/Packages/XcodeExtensionSupport.pkg" -target /
fi

# Install CoreTypes.pkg
# /Applications/Xcode.app/Contents/Resources/Packages/CoreTypes.pkg
if [[ -e "/Applications/Xcode.app/Contents/Resources/Packages/CoreTypes.pkg" ]]
then
  echo " ------- "  
  echo "Installing CoreTypes Package at:  $(date)"
  /usr/sbin/installer -dumplog -verbose -pkg "/Applications/Xcode.app/Contents/Resources/Packages/CoreTypes.pkg" -target /
fi

# Install XcodeSystemResources.pkg
if [[ -e "/Applications/Xcode.app/Contents/Resources/Packages/XcodeSystemResources.pkg" ]]
then
  echo " ------- "  
  echo "Installing XCode System Resources package at:  $(date)"
  /usr/sbin/installer -dumplog -verbose -pkg "/Applications/Xcode.app/Contents/Resources/Packages/XcodeSystemResources.pkg" -target /  
fi

# Install Command Line Tools.
if [[ /usr/bin/xcode-select ]]
then
  echo " ------- "
  echo "Installing XCode Command Line Tools at:  $(date)" 
  /usr/bin/xcode-select --install
fi

# Allow any member of _developer to install Apple-provided software.
# be sure you really want to do this.
# as of April 2022 - I don't want to allow this ... so commented out!
#/usr/bin/security authorizationdb write system.install.apple-software authenticate-developer


# Set sleep timers back to 45 minutes
echo " ------- "
echo "Setting sleep timers back to 45 minutes at $(date)"
pmset -a sleep 45
pmset -a disksleep 45
pmset -a displaysleep 45

# Run Jamf inventory to record Xcode installed and drop mac out of smart group for install
echo " ------- "
echo "Running Jamf Inventory at $(date)"
jamf recon

echo " ------- "
echo "Script ended: Xcode Configure at  $(date)"
echo " ------- "

 

 

11 REPLIES 11

mm2270
Legendary Contributor III

Yes, I've also had to go the route of using Packages.app to package up Xcode. I never tried it, but it's possible Composer can create a DMG format for it. But fuhgeddaboudit for creating a .pkg format. Composer just completely hangs and dies if you try packaging Xcode in that format. It's more of an OS issue than a Composer issue though to be fair.

I also have a "post install" script run that does many of the same actions as yours. However, I just have it run any .pkgs it finds inside the /Applications/Xcode.app/Contents/Resources/ path, rather than doing them one at a time, since I feel reasonably sure that all of those in that location are needed for it to function fully.

That being said, I just wanted to mention that you probably should be urging your Xcode users to upgrade to Monterey (if necessary) and go with Xcode 13.3. Not sure if you're aware, but there's a vulnerable version of log4j included in any older versions of the application. Or at least older Xcode 13.x versions. Not sure if the issue goes back to version 12 or not. So I wouldn't recommend continuing to put Xcode 13.2.1 out there given this, unless you really have no other choice.

You can read more about that here in Apple's release notes: Xcode 13.3 Release Notes | Apple Developer Documentation

lynnaj
New Contributor III

Thanks for reading my post!

I typically do try to run the newest versions of applications but for many faculty that causes issues, particularly with buggy initial releases of new Xcode.  I probably should have added that these "lab" macs are cut off from both the internet and the rest of our network in such a way as to reduce our vulnerability to a bunch of threats.  Clearly nothing is perfect so I will try to reinforce the need to run a newer version of Xcode.  Perhaps we'll even get a 13.3.1 version of Xcode before I have to deploy this in a month or so ...

 

Anyway - Would you mind posting your Xcode post install script (or a script snip-it)?  Finding and installing all those internal Xcode packages sounds very useful as part of a script.  In some ways that "future proofs" this process as I'm sure Apple will add more packages as time goes on. We don't allow students to run as administrators so they won't be able to install anything on their own. (Standard users only is another tactic we use to keep the malware at bay.)

mm2270
Legendary Contributor III

Hi there. Sure, this is the code block that takes care of installing all packages in that Resources folder, and accepts the license and applies the first run setting. It's a little different than what you're doing, but has the same result as I understand it.
Also, I'm only putting the logged in user into the _developer group, but that's because our Macs are single use systems. For a lab setup like you described, it makes sense to add the everyone group into _developer.

## Accept license and enable first run items
if [ -e "/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild" ]; then
	for PKG in $(/usr/bin/find "/Applications/Xcode.app/Contents/Resources/Packages" -name "*.pkg"); do
    	/bin/echo "Installing resource package: $PKG"
    	/usr/sbin/installer -pkg "$PKG" -target /
        /bin/echo "Installation for \"$PKG\" complete"
	done
    ## Accept Xcode license and run first launch
    /bin/echo "Running first launch setup and accepting license"
	/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -license accept
	/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -runFirstLaunch
else
	/bin/echo "Error: Xcode does not appear to be installed on this system."
    exit 1
fi

The above is only a section of the script. After the above I add the user into the _developer group, and enable DevToolsSecurity.

And totally understood on the need to keep them on a slightly older version for right now. Thanks for pointing out there may be some bugginess in 13.3. I'll keep an eye on the 13.3.1 currently in beta to see if we want to move to that once it releases. Sometimes situations aren't ideal and you just have to work around stuff until the situation improves. It sounds like you've got it covered though. πŸ™‚

greenet3
New Contributor II

I just want to say thank you to you both for this. Been trying to install Xcode 13.4.1 (I think that's the latest), and it's been giving me nightmares. Using VPP is a no-go for us, and it usually hangs, or just straight fails. However, I can pull it directly from the App Store. 

 

I'll definitely be putting these scripts in our environment as our semester is starting up soon. 

lynnaj
New Contributor III

I'm glad this is/was helpful to you!

GabeShack
Valued Contributor III

Hey @mm2270 ,

Any chance you could share the whole script you use for single user device Xcode installs?  The one from the past I've been using no longer works properly, and the only updated version I found was @ryan_ball 's script for installing the current command line tools which I'd assume you both do as well?

#!/bin/bash

# Installing the Xcode command line tools on 10.7.x or higher

# Save current IFS state

OLDIFS=$IFS

IFS='.' read osvers_major osvers_minor osvers_dot_version <<< "$(/usr/bin/sw_vers -productVersion)"

# restore IFS to previous state

IFS=$OLDIFS

cmd_line_tools_temp_file="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress"

# Installing the latest Xcode command line tools on 10.9.x or higher

if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -ge 9 ) || ( ${osvers_major} -ge 11 && ${osvers_minor} -ge 0 ) ]]; then

	# Create the placeholder file which is checked by the softwareupdate tool 
	# before allowing the installation of the Xcode command line tools.
	
	touch "$cmd_line_tools_temp_file"
	
	# Identify the correct update in the Software Update feed with "Command Line Tools" in the name for the OS version in question.

	if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -ge 15 ) || ( ${osvers_major} -ge 11 && ${osvers_minor} -ge 0 ) ]]; then
	   cmd_line_tools=$(softwareupdate -l | awk '/\*\ Label: Command Line Tools/ { $1=$1;print }' | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 9-)	
	elif [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -gt 9 ) ]] && [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -lt 15 ) ]]; then
	   cmd_line_tools=$(softwareupdate -l | awk '/\*\ Command Line Tools/ { $1=$1;print }' | grep "$osvers_minor" | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 2-)
	elif [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 9 ) ]]; then
	   cmd_line_tools=$(softwareupdate -l | awk '/\*\ Command Line Tools/ { $1=$1;print }' | grep "Mavericks" | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 2-)
	fi
	
	# Check to see if the softwareupdate tool has returned more than one Xcode
	# command line tool installation option. If it has, use the last one listed
	# as that should be the latest Xcode command line tool installer.
	
	if (( $(grep -c . <<<"$cmd_line_tools") > 1 )); then
	   cmd_line_tools_output="$cmd_line_tools"
	   cmd_line_tools=$(printf "$cmd_line_tools_output" | tail -1)
	fi
	
	#Install the command line tools
	
	softwareupdate -i "$cmd_line_tools" --verbose
	
	# Remove the temp file
	
	if [[ -f "$cmd_line_tools_temp_file" ]]; then
	  rm "$cmd_line_tools_temp_file"
	fi
fi

# Installing the latest Xcode command line tools on 10.7.x and 10.8.x

# on 10.7/10.8, instead of using the software update feed, the command line tools are downloaded
# instead from public download URLs, which can be found in the dvtdownloadableindex:
# https://devimages.apple.com.edgekey.net/downloads/xcode/simulators/index-3905972D-B609-49CE-8D06-51ADC78E07BC.dvtdownloadableindex

if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 7 ) || ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 8 ) ]]; then

	if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 7 ) ]]; then	
	    DMGURL=http://devimages.apple.com/downloads/xcode/command_line_tools_for_xcode_os_x_lion_april_2013.dmg
	fi
	
	if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 8 ) ]]; then
	     DMGURL=http://devimages.apple.com/downloads/xcode/command_line_tools_for_osx_mountain_lion_april_2014.dmg
	fi

		TOOLS=cltools.dmg
		curl "$DMGURL" -o "$TOOLS"
		TMPMOUNT=`/usr/bin/mktemp -d /tmp/clitools.XXXX`
		hdiutil attach "$TOOLS" -mountpoint "$TMPMOUNT" -nobrowse
		# The "-allowUntrusted" flag has been added to the installer
		# command to accomodate for now-expired certificates used
		# to sign the downloaded command line tools.
		installer -allowUntrusted -pkg "$(find $TMPMOUNT -name '*.mpkg')" -target /
		hdiutil detach "$TMPMOUNT"
		rm -rf "$TMPMOUNT"
		rm "$TOOLS"
fi

exit 0

 

Thanks for any info you can provide! 

Gabe Shackney
Princeton Public Schools

mm2270
Legendary Contributor III

Hi @GabeShack Sure, here is the script I'm using right now, though I have to test it against the latest and greatest version of Xcode to be sure nothing needs to be updated. If you find any issues with it, I'd love to know.

Also, as you'll see, I'm not downloading and installing Xcode CL tools in this, since with our user authenticated proxy, it would be very difficult to download that using a script. So I install that separately by grabbing the pkg from Apple and using that in a policy. The script below is only installing the "extra" packages embedded inside the Xcode app bundle and enabling DevToolsSecurity and adding the current user to the local _developer group.

 

#!/bin/sh

## Script Name:				Enable-Xcode-First-Launch
## Purpose:					Auto accepts the license agreement and first launch items for Xcode. Also adds the current logged in user to the _developer group.

logged_in_user=$(/usr/sbin/scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ {print $3}')

## Accept license and enable first run items
if [ -e "/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild" ]; then
	for PKG in $(/usr/bin/find "/Applications/Xcode.app/Contents/Resources/Packages" -name "*.pkg"); do
    	/bin/echo "Installing resource package: $PKG"
    	/usr/sbin/installer -pkg "$PKG" -target /
        /bin/echo "Installation for \"$PKG\" complete"
	done
    ## Accept Xcode license and run first launch
    /bin/echo "Running first launch setup and accepting license"
	/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -license accept
	/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -runFirstLaunch
else
	/bin/echo "Error: Xcode does not appear to be installed on this system."
    exit 1
fi

## Check the group membership of the current user for _developer
user_dev_status=$(/usr/sbin/dseditgroup -o checkmember -m $logged_in_user _developer > /dev/null 2>&1; echo $?)
## Check that DevToolsSecurity is enabled
dev_tools_status=$(/usr/sbin/DevToolsSecurity -status | awk '{print $NF}')

## Take action if needed for user account
if [ "$user_dev_status" != "0" ]; then
	/bin/echo "User account $logged_in_user is not in the local developer group. Adding account to _developer now..."
	/usr/sbin/dseditgroup -o edit -a $logged_in_user -t user _developer
    if [ "$?" == "0" ]; then
    	/bin/echo "User account $logged_in_user successfully added to _developer."
    else
    	/bin/echo "Error! User account $logged_in_user could not be added to _developer."
    fi
fi

## Take action if needed for 
if [[ ! "$dev_tools_status" =~ "enabled." ]]; then
	/bin/echo "DevToolsSecurity is not enabled. Enabling now..."
	/usr/sbin/DevToolsSecurity -enable
    if [ "$?" == "0" ]; then
    	/bin/echo "DevToolsSecurity successfully enabled."
    else
    	/bin/echo "Error! DevToolsSecurity could not be enabled."
    fi
fi

 

GabeShack
Valued Contributor III

@mm2270 Question, have you (or anyone else for that matter) tried packaging up the .xip file that apple uses on their developer download, since its only about 7gb instead of 23gb.  Im trying to see if i can put that .xip file directly into /Applications, then script a way to unxip it and then run the scripts for command line tool installs and your above script. Im playing now with the Packages.app to see what size it makes Xcode when built.  

So frustrating that Apple makes it this hard to install their development software on managed devices....

Gabe Shackney
Princeton Public Schools

efil4xiN
Contributor

You can use quickpkg to create a Package for Xcode

GabeShack
Valued Contributor III

So I tackled this a bit differently and just tested it out on a student device(standard user device).  I reused a script that @mm2270 helped me with awhile ago.

Its a script that calls other Jamf policies and puts a window up letting the user know whats happening at each step (with a custom icon using jamf helper):

 

#!/bin/zsh
#Keep Machine from going to sleep
caffeinate -i -s -d -t 2400 &
## Static paths to jamfHelper and the icon
jamf_helper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
icon="/Library/Desktop Pictures/PPSLogo.jpg"

## Bash array containing the custom triggers (events)
custom_triggers=(
xcodeinstall
CommandLineTools
xcode1stRun
)

## Use the array length to figure out how many steps we have automatically
total_steps=${#custom_triggers[@]}

## Function that calls each policy trigger and customizes the message during each loop
## Uses a case statement to determine the heading text for each custom event trigger
function callPolicy ()
{

## This case statement determines which heading text we need to use in jamfHelper for each policy trigger
case $trigger in
    xcodeinstall)
	heading_text="Caching Xcode(Upto30min)"
	;;
    CommandLineTools)
	heading_text="Installing CommandLineTools"
    ;;
    xcode1stRun)
	heading_text="Finishing Xcode Install"
	;;
esac

## Call jamfHelper and customize the display slightly
"$jamf_helper" \
	-windowType utility \
	-title "Xcode Installer" \
	-heading "$heading_text" \
	-description "Completing Step $i of $total_steps" \
	-icon "$icon" &
	## Call the Jamf policy by it's event trigger
	/usr/local/bin/jamf policy -event "$trigger"
	/bin/sleep 2
	## Shut down the jamfHelper background process to get ready for the next one
	killall jamfHelper
}

## Start the loop over the custom event triggers array
## For each one it processes, it calls the function above
## We increase the count of the "step" after each one completes
i=1;
for trigger in "${custom_triggers[@]}"; do
	echo "Calling policy trigger $trigger"
	callPolicy
	let i=$((i+1))
done

## When we get to this point, we're done with the policy loop, so show the installs complete message here
"$jamf_helper" \
	-windowType utility \
	-title "XCode Installer" \
	-heading "Completed Installs" \
	-description "Xcode Install Is now complete" \
	-icon "$icon" \
	-timeout 2

## Final message
"$jamf_helper" \
	-windowType utility \
	-lockHUD \
	-icon "$icon" \
	-title "Xcode Install Complete" \
	-heading "Xcode Install Complete" \
	-description "Click Continue and open Xcode from the Dock" \
	-button1 "Continue" -defaultButton 1


exit 0

 

It requires separate policies with manual triggers.  I packaged up the Xcode.xip file in composer so its right in /Applicaitons.  I then call for the policy to install the compressed (captured with composer) Xcode.xip file.  Then I call a policy to un zip it with the command(this takes quite awhile to unzip on intel machines):

This took too long to unzip so I packaged the file in Packages.app like above and removed the below command:

 

#open -W /Applications/Xcode_14.xip

 

Which waits for the app to close before completing the command.

Then I call the policy to run the script to download Command Line Tools:

 

#!/bin/bash

# Installing the Xcode command line tools on 10.7.x or higher

# Save current IFS state

OLDIFS=$IFS

IFS='.' read osvers_major osvers_minor osvers_dot_version <<< "$(/usr/bin/sw_vers -productVersion)"

# restore IFS to previous state

IFS=$OLDIFS

cmd_line_tools_temp_file="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress"

# Installing the latest Xcode command line tools on 10.9.x or higher

if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -ge 9 ) || ( ${osvers_major} -ge 11 && ${osvers_minor} -ge 0 ) ]]; then

	# Create the placeholder file which is checked by the softwareupdate tool 
	# before allowing the installation of the Xcode command line tools.
	
	touch "$cmd_line_tools_temp_file"
	
	# Identify the correct update in the Software Update feed with "Command Line Tools" in the name for the OS version in question.

	if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -ge 15 ) || ( ${osvers_major} -ge 11 && ${osvers_minor} -ge 0 ) ]]; then
	   cmd_line_tools=$(softwareupdate -l | awk '/\*\ Label: Command Line Tools/ { $1=$1;print }' | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 9-)	
	elif [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -gt 9 ) ]] && [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -lt 15 ) ]]; then
	   cmd_line_tools=$(softwareupdate -l | awk '/\*\ Command Line Tools/ { $1=$1;print }' | grep "$osvers_minor" | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 2-)
	elif [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 9 ) ]]; then
	   cmd_line_tools=$(softwareupdate -l | awk '/\*\ Command Line Tools/ { $1=$1;print }' | grep "Mavericks" | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 2-)
	fi
	
	# Check to see if the softwareupdate tool has returned more than one Xcode
	# command line tool installation option. If it has, use the last one listed
	# as that should be the latest Xcode command line tool installer.
	
	if (( $(grep -c . <<<"$cmd_line_tools") > 1 )); then
	   cmd_line_tools_output="$cmd_line_tools"
	   cmd_line_tools=$(printf "$cmd_line_tools_output" | tail -1)
	fi
	
	#Install the command line tools
	
	softwareupdate -i "$cmd_line_tools" --verbose
	
	# Remove the temp file
	
	if [[ -f "$cmd_line_tools_temp_file" ]]; then
	  rm "$cmd_line_tools_temp_file"
	fi
fi

# Installing the latest Xcode command line tools on 10.7.x and 10.8.x

# on 10.7/10.8, instead of using the software update feed, the command line tools are downloaded
# instead from public download URLs, which can be found in the dvtdownloadableindex:
# https://devimages.apple.com.edgekey.net/downloads/xcode/simulators/index-3905972D-B609-49CE-8D06-51ADC78E07BC.dvtdownloadableindex

if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 7 ) || ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 8 ) ]]; then

	if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 7 ) ]]; then	
	    DMGURL=http://devimages.apple.com/downloads/xcode/command_line_tools_for_xcode_os_x_lion_april_2013.dmg
	fi
	
	if [[ ( ${osvers_major} -eq 10 && ${osvers_minor} -eq 8 ) ]]; then
	     DMGURL=http://devimages.apple.com/downloads/xcode/command_line_tools_for_osx_mountain_lion_april_2014.dmg
	fi

		TOOLS=cltools.dmg
		curl "$DMGURL" -o "$TOOLS"
		TMPMOUNT=`/usr/bin/mktemp -d /tmp/clitools.XXXX`
		hdiutil attach "$TOOLS" -mountpoint "$TMPMOUNT" -nobrowse
		# The "-allowUntrusted" flag has been added to the installer
		# command to accomodate for now-expired certificates used
		# to sign the downloaded command line tools.
		installer -allowUntrusted -pkg "$(find $TMPMOUNT -name '*.mpkg')" -target /
		hdiutil detach "$TMPMOUNT"
		rm -rf "$TMPMOUNT"
		rm "$TOOLS"
fi

exit 0

 

Then I call the script @mm2270 provided:

 

#!/bin/sh

## Script Name:				Enable-Xcode-First-Launch
## Purpose:					Auto accepts the license agreement and first launch items for Xcode. Also adds the current logged in user to the _developer group.

logged_in_user=$(/usr/sbin/scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ {print $3}')

## Accept license and enable first run items
if [ -e "/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild" ]; then
	for PKG in $(/usr/bin/find "/Applications/Xcode.app/Contents/Resources/Packages" -name "*.pkg"); do
    	/bin/echo "Installing resource package: $PKG"
    	/usr/sbin/installer -pkg "$PKG" -target /
        /bin/echo "Installation for \"$PKG\" complete"
	done
    ## Accept Xcode license and run first launch
    /bin/echo "Running first launch setup and accepting license"
	/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -license accept
	/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -runFirstLaunch
else
	/bin/echo "Error: Xcode does not appear to be installed on this system."
    exit 1
fi

## Check the group membership of the current user for _developer
user_dev_status=$(/usr/sbin/dseditgroup -o checkmember -m $logged_in_user _developer > /dev/null 2>&1; echo $?)
## Check that DevToolsSecurity is enabled
dev_tools_status=$(/usr/sbin/DevToolsSecurity -status | awk '{print $NF}')

## Take action if needed for user account
if [ "$user_dev_status" != "0" ]; then
	/bin/echo "User account $logged_in_user is not in the local developer group. Adding account to _developer now..."
	/usr/sbin/dseditgroup -o edit -a $logged_in_user -t user _developer
    if [ "$?" == "0" ]; then
    	/bin/echo "User account $logged_in_user successfully added to _developer."
    else
    	/bin/echo "Error! User account $logged_in_user could not be added to _developer."
    fi
fi

## Take action if needed for 
if [[ ! "$dev_tools_status" =~ "enabled." ]]; then
	/bin/echo "DevToolsSecurity is not enabled. Enabling now..."
	/usr/sbin/DevToolsSecurity -enable
    if [ "$?" == "0" ]; then
    	/bin/echo "DevToolsSecurity successfully enabled."
    else
    	/bin/echo "Error! DevToolsSecurity could not be enabled."
    fi
fi

 

It seems to be working pretty well albeit very slowly unzipping that file on intel machines.  On Apple Silicon machines its much faster.  

@mm2270 Its not done yet on my test machine, but I'll let you know if I see any errors with the script you provided for the first launch using Xcode 14.

Gabe Shackney
Princeton Public Schools

greenet3
New Contributor II

 I've been using this script for Xcode 13.4.1 on Monterey. 

It seems on Ventura the xattr command is no longer valid without making some system changes. I keep getting "Operation not permitted." However, everything else works, and I can open Xcode 14.1 as a standard user. Since I'm not a every-day user of Xcode, would this be a problem if called from a policy? I can get to the IDE, but not sure if something else needs to be done before everything works.