├── .gitignore ├── assets ├── setup-wizard.png ├── setup-complete.png └── setup-proceeding.png ├── autopkg-repo-list.txt ├── README.md ├── setup-dialog.command └── autopkg-setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | autopkg-preferences--*.yaml 2 | credentials-*.yaml 3 | *.pyc 4 | config/* 5 | config 6 | -------------------------------------------------------------------------------- /assets/setup-wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grahampugh/AutoPkgSetup/HEAD/assets/setup-wizard.png -------------------------------------------------------------------------------- /assets/setup-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grahampugh/AutoPkgSetup/HEAD/assets/setup-complete.png -------------------------------------------------------------------------------- /assets/setup-proceeding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grahampugh/AutoPkgSetup/HEAD/assets/setup-proceeding.png -------------------------------------------------------------------------------- /autopkg-repo-list.txt: -------------------------------------------------------------------------------- 1 | ahousseini-recipes 2 | andredb90-recipes 3 | andrewvalentine-recipes 4 | apizz-recipes 5 | blackthroat-recipes 6 | bochoven-recipes 7 | cgerke-recipes 8 | crystalllized-recipes 9 | dataJAR-recipes 10 | drewdiver-recipes 11 | eth-its/autopkg-mac-recipes-yaml 12 | faumac-recipes 13 | gerardkok-recipes 14 | grahampugh-recipes 15 | hansen-m-recipes 16 | hjuutilainen-recipes 17 | homebysix-recipes 18 | its-unibas-recipes 19 | jbaker10-recipes 20 | jlehikoinen-recipes 21 | joshua-d-miller-recipes 22 | justinrummel-recipes 23 | keeleysam-recipes 24 | killahquam-recipes 25 | MLBZ521-recipes 26 | moofit-recipes 27 | n8felton-recipes 28 | neilmartin83-recipes 29 | nmcspadden-recipes 30 | novaksam-recipes 31 | nstrauss-recipes 32 | nzmacgeek-recipes 33 | paul-cossey-recipes 34 | peshay-recipes 35 | peterkelm-recipes 36 | precursorca-recipes 37 | recipes 38 | rtrouton-recipes 39 | scriptingosx-recipes 40 | sebtomasi-recipes 41 | smithjw-recipes 42 | tbridge-recipes 43 | wardsparadox-recipes 44 | kevinmcox-recipes 45 | swy-recipes -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoPkgSetup 2 | 3 | A script to automatically install [AutoPkg] and optionally configure [JamfUploader] for immediate use. 4 | 5 | ## What does it do? 6 | 7 | * Installs command line tools if not present (because `git` is required for 8 | AutoPkg) 9 | * Downloads, installs and configures the latest version of AutoPkg 10 | * Optionally configures JamfUploader 11 | 12 | The script is idempotent. It is safe to run if the Xcode Command Line Tools and 13 | AutoPkg are already installed. They will only be updated if 14 | they are out of date. Any existing AutoPkg repos will also be updated, with the 15 | caveat that all the repos you want should be in your autopkg-repo-list file. 16 | 17 | ## Prerequisites for JamfUploader to work 18 | 19 | Create a user on each JSS Instance with the following credentials 20 | (**System Settings** => **JSS User Accounts & Groups**): 21 | 22 | * **Account:** 23 | * Username: `AutoPkg` 24 | * Access Level: `Full Access` 25 | * Privilege Set: `Custom` 26 | * Access Status: `Enabled` 27 | * Full Name: `AutoPkg JSSImporter` 28 | * Email Address: `jamfadmin@myorg.com` 29 | * Password: `ChangeMe!!!` 30 | * **Privileges:** 31 | * Categories: `Create` `Read` `Update` 32 | * Computer Extension Attributes: `Create` `Read` `Update` 33 | * File Share Distribution Points: `Read` 34 | * Packages: `Create` `Read` `Update` 35 | * Policies: `Create` `Read` `Update` 36 | * Scripts: `Create` `Read` `Update` 37 | * Smart Computer Groups: `Create` `Read` `Update` 38 | * Static Computer Groups: `Create` `Read` `Update` 39 | 40 | You also need to know the password that the JSS uses to connect to the 41 | distribution point. 42 | 43 | ## Download 44 | 45 | Since one of the steps of this script is to install git, you'll perhaps not be able to git clone this script. So download the ZIP archive from the GitHub page, or use the following command to obtain the latest commit: 46 | 47 | ``` 48 | curl -L "https://github.com/grahampugh/AutoPkgSetup/archive/refs/heads/main.zip" -o ~/Downloads/autopkg-setup.zip 49 | ``` 50 | 51 | Then unzip the downloaded zip file: 52 | 53 | ``` 54 | unzip ~/Downloads/autopkg-setup.zip 55 | ``` 56 | 57 | ## Using the Setup Wizard 58 | 59 | To run the script using a swiftDialog-based Setup Wizard, run the file setup-dialog.command from Finder or run `open setup-dialog.command`. 60 | 61 | Follow the instructions in the dialog window to proceed. 62 | 63 | ![Setup Wizard](./assets/setup-wizard.png) 64 | 65 | ## Running the script 66 | 67 | Run the script as the regular user (not as root/sudo). 68 | 69 | Run with no options to: 70 | 71 | * Install the Xcode Command Line Tools 72 | * Download and install AutoPkg 73 | * Create the prefs file in the default location (`~/Library/Preferences/com.github.autopkg.plist`) 74 | * Add the `grahampugh-recipes` repo 75 | 76 | ``` 77 | ./autopkg-setup.sh 78 | ``` 79 | 80 | Additional options are as follows. 81 | 82 | ### Use the jamf-upload repo instead of the grahampugh-recipes repo 83 | 84 | Adding the jamf-upload repo means you get the latest features of JamfUploader. It also allows you to use the `jamf-upload.sh` script which can take advantage of the JamfUploader processors without needing recipes. For more information about `jamf-upload.sh`, see the [jamf-upload.sh wiki page](). 85 | 86 | To add the jamf-upload repo, add the `--jamf-uploader-repo` flag: 87 | 88 | ``` 89 | ./autopkg-setup.sh --jamf-uploader-repo 90 | ``` 91 | 92 | If you only intend to install AutoPkg for use with `jamf-upload.sh`, you don't need any more parameters. 93 | 94 | ### Force reinstallation of AutoPkg 95 | 96 | If you want to force the reinstallation of AutoPkg, for example to upgrade AutoPkg, use the `-f` or `--force` option. 97 | 98 | ### Allow recipes to run without trust 99 | 100 | If you want to allow recipes to run without failing due to no trust, use the `-x` or `--fail` option. 101 | 102 | ### Install the latest pre-release version of AutoPkg 103 | 104 | If you want to force the installation of the latest pre-release version of AutoPkg, use the `-b` or `--beta` option. 105 | 106 | ### Supply an existing prefs file 107 | 108 | To supply an pre-made prefs file, use the `--prefs` option and specify a path, e.g. `./autopkg-setup.sh --prefs /path/to/com.myorg.autopkg.prefs`. 109 | 110 | ### Replace an existing prefs file 111 | 112 | To delete any existing prefs file and start fresh, add the `--replace-prefs` option. 113 | 114 | ### Add a GitHub token 115 | 116 | To add a GitHub token to aid with AutoPkg searches, add the `--github-token` option and specify the token, e.g. `./autopkg-setup.sh --github-token MY_GITHUB_TOKEN`. 117 | 118 | ### Add repos from a repo list 119 | 120 | To add (or update) repos from a repo-list, add the `--repo-list` option and specify the path to the list, e.g. `./autopkg-setup.sh --repo-list /path/to/repolist.txt`. 121 | 122 | ### Add necessary repos for a recipe list 123 | 124 | To ensure all dependencies for your recipe list are added to your repo list, add the `--recipe-list` option and specify the path to the list, e.g. `./autopkg-setup.sh --recipe-list /path/to/recipelist.txt`. This will run `autopkg info -p` for all recipes in the list and attempt to add all parent repos that are not already added. Note that this option is currently fragile due to problems with GitHub searches. 125 | 126 | ### Add a private repo to the AutoPkg search list 127 | 128 | To add a private repo, supply the path to the repo with `--private-repo /path/to/private-repo` and the URL of the repo with `--private-repo-url https://my.git.server/reponame`. 129 | 130 | ## Configure JamfUploader 131 | 132 | To configure JamfUploader, supply the Jamf Pro server URL, e.g. `--jss-url "https://my.jamfcloud.com"`. 133 | 134 | You can supply the API user from the command line with the `--jss-user MY_USERNAME` option. If you use the `--jss-url` option but do not supply a value for `--jss-user`, and it is not already set in the AutoPkg prefs, you will be asked to supply it. 135 | 136 | You can supply the API user's password from the command line with the `--jss-pass MY_PASSWORD` option. If you do not supply this value and it is not already set in the AutoPkg prefs, you will be asked to supply it. 137 | 138 | Jamf Cloud Distribution Point users do not need to supply any additional keys. 139 | 140 | To set `jcds2_mode`, add the `-j` or `--jcds2-mode` option. 141 | 142 | If you have a local FileShare Distribution Point, supply the SMB server's full URL including Share name, e.g. `--smb-url "smb://my.jamf-dp.com/ShareName"`. The share must be a top level share. 143 | 144 | You can supply the SMB user from the command line with the `--smb-user MY_SMB_USERNAME` option. If you use the `--smb-url` option but do not supply a value for `--smb-user`, and it is not already set in the AutoPkg prefs, you will be asked to supply it. 145 | 146 | You can supply the API user's password from the command line with the `--smb-pass MY_SMB_PASSWORD` option. If you do not supply this value and it is not already set in the AutoPkg prefs, you will be asked to supply it. 147 | 148 | ## Configure a Slack webhook 149 | 150 | To configure a Slack webhook, supply the hook with `--slack-webhook https://my.slack.webhook/url`. 151 | 152 | To set a username that Slack will report as, supply it with `--slack-user SLACK_USERNAME`. 153 | 154 | [AutoPkg]: https://github.com/autopkg/autopkg 155 | [JamfUploader]: https://github.com/grahampugh/jamf-upload/wiki/JamfUploader-AutoPkg-Processors 156 | -------------------------------------------------------------------------------- /setup-dialog.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | : </dev/null ; then 55 | echo "SwiftDialog is not installed. App will be installed now....." 56 | dialog_install 57 | else 58 | echo "SwiftDialog is installed. Checking installed version....." 59 | 60 | dialog_installed_version=$("$dialog_bin" -v | sed 's/\.[0-9]*$//') 61 | 62 | # obtain the tag 63 | dialog_latest_version=$(curl -sL -H "Accept: application/json" "$swiftdialog_api_url" | awk -F '"' '/tag_name/ { print $4; exit }') 64 | if [[ ! $dialog_latest_version ]]; then 65 | echo "Could not obtain latest version information, proceeding without check..." 66 | elif [[ "$dialog_installed_version" != "${dialog_latest_version//v/}" ]]; then 67 | echo "Dialog needs updating (v$dialog_installed_version older than $dialog_latest_version)" 68 | dialog_install 69 | sleep 3 70 | else 71 | echo "Dialog is up to date. Continuing...." 72 | fi 73 | fi 74 | } 75 | 76 | dialog_install() { 77 | 78 | # install 79 | if /usr/bin/curl -L "$dialog_download_url" -o "$tmpdir/dialog.pkg" ; then 80 | if sudo installer -pkg "$tmpdir/dialog.pkg" -target / ; then 81 | dialog_string=$("$dialog_bin" --version) 82 | else 83 | echo "swiftDialog installation failed" 84 | exit 1 85 | fi 86 | else 87 | echo "swiftDialog download failed" 88 | exit 1 89 | fi 90 | # check it did actually get downloaded 91 | if [[ -d "$dialog_app" && -f "$dialog_bin" ]]; then 92 | echo "swiftDialog v$dialog_string is installed" 93 | else 94 | echo "Could not download swiftDialog." 95 | exit 1 96 | fi 97 | 98 | /bin/rm "$tmpdir/dialog.pkg" ||: 99 | } 100 | 101 | ############################################################## 102 | # This function sends a command to our command file, and sleeps briefly to avoid race conditions 103 | ############################################################## 104 | 105 | dialog_command() 106 | { 107 | echo "$@" >> "$dialog_log" 2>/dev/null & sleep 0.1 108 | } 109 | 110 | dialog_command_with_output() 111 | { 112 | "$dialog_bin" "$@" 2>/dev/null > "$dialog_output" & sleep 0.1 113 | } 114 | 115 | run_dialog() { 116 | dialog_args=( 117 | --commandfile 118 | "$dialog_log" 119 | --title "AutoPkg Setup Wizard" 120 | --position centre 121 | --moveable 122 | --icon "https://avatars.githubusercontent.com/u/5170557?s=200&v=4" 123 | --message "Choose the installation options below. \n\nYou can optionally supply a Jamf Pro URL, API username and password below. This is not required but will set up AutoPkg with a default JSS." \ 124 | --button1text "Continue" 125 | --button2text "Quit" 126 | --alignment left 127 | --infobox '[github.com/autopkg/autopkg](https://github.com/autopkg/autopkg)' 128 | --messagefont 'name=Arial,size=16' 129 | --textfield 'JAMF URL,prompt=https://example.jamfcloud.com' 130 | --textfield 'JAMF Username,prompt=' 131 | --textfield 'JAMF Password,secure' 132 | --textfield 'GitHub Token,prompt=' 133 | --checkbox "Allow recipes to run without trust information" 134 | --checkbox "Force AutoPkg reinstallation" 135 | --checkbox "Install AutoPkg beta version" 136 | --checkbox "Use jamf-upload repo (beta)" 137 | --checkboxstyle switch 138 | --height 500 139 | --json 140 | --ontop 141 | ) 142 | echo "quit:" >> "$dialog_log" 143 | "$dialog_bin" "${dialog_args[@]}" 2>/dev/null > "$dialog_output" 144 | if [[ $? -eq 2 ]]; then 145 | echo "User cancelled dialog so exiting..." 146 | exit 0 147 | fi 148 | } 149 | 150 | progress_dialog() { 151 | # show progress 152 | dialog_args=( 153 | --commandfile 154 | "$dialog_log" 155 | --title "AutoPkg Setup Wizard" 156 | --position centre 157 | --moveable 158 | --icon "https://avatars.githubusercontent.com/u/5170557?s=200&v=4" 159 | --message "Installation is proceeding...\n\nDepending on the options chosen, this will\n\n* Check that Xcode Command Line Tools are installed\n* Install or update AutoPkg (**NOTE: this part requires admin rights - please check the Terminal window and enter password if required**)\n* Set the path to Git in the AutoPkg preference file\n* Set recipes to fail or proceed if untrusted\n* Add the jamf-upload and grahampugh-recipes repos\n* Update all repos" 160 | --button1disabled 161 | --progress 1 162 | --alignment left 163 | --infobox '[github.com/autopkg/autopkg](https://github.com/autopkg/autopkg)' 164 | --messagefont 'name=Arial,size=16' 165 | --ontop 166 | ) 167 | "$dialog_bin" "${dialog_args[@]}" 2>/dev/null & sleep 0.1 168 | 169 | echo "progresstext: Installing and configuring AutoPkg" >> "$dialog_log" 170 | echo "progress: 0" >> "$dialog_log" 171 | } 172 | 173 | done_dialog() { 174 | # done dialog 175 | dialog_args=( 176 | --commandfile 177 | "$dialog_log" 178 | --title "AutoPkg Setup Wizard" 179 | --position centre 180 | --moveable 181 | --icon "https://avatars.githubusercontent.com/u/5170557?s=200&v=4" 182 | --message "Installation is now complete." \ 183 | --button1text "OK" 184 | --alignment left 185 | --infobox '[github.com/autopkg/autopkg](https://github.com/autopkg/autopkg)' 186 | --messagefont 'name=Arial,size=16' 187 | --ontop 188 | ) 189 | echo "quit:" >> "$dialog_log" & sleep 0.1 190 | "$dialog_bin" "${dialog_args[@]}" 2>/dev/null 191 | } 192 | 193 | rootCheck 194 | dialog_check 195 | run_dialog 196 | 197 | ############################################################## 198 | # Gather information from the dialog 199 | ############################################################## 200 | 201 | # TEMP 202 | cat "$dialog_output" 203 | echo 204 | # TEMP 205 | 206 | JSS_URL=$(plutil -extract "JAMF URL" raw "$dialog_output" 2>/dev/null) 207 | JSS_API_USER=$(plutil -extract "JAMF Username" raw "$dialog_output" 2>/dev/null) 208 | JSS_API_PW=$(plutil -extract "JAMF Password" raw "$dialog_output" 2>/dev/null) 209 | GITHUB_TOKEN=$(plutil -extract "GitHub Token" raw "$dialog_output" 2>/dev/null) 210 | FORCE_AUTOPKG=$(plutil -extract "Force AutoPkg reinstallation" raw "$dialog_output") 211 | INSTALL_AUTOPKG_BETA=$(plutil -extract "Install AutoPkg beta version" raw "$dialog_output") 212 | DO_NOT_FAIL_RECIPES_WITHOUT_TRUST_INFO=$(plutil -extract "Allow recipes to run without trust information" raw "$dialog_output") 213 | JAMFUPLOAD_REPO=$(plutil -extract "Use jamf-upload repo (beta)" raw "$dialog_output") 214 | 215 | # if [[ $USER == "" ]] || [[ $PASSWORD == "" ]] || [[ $URL == "" ]]; then 216 | # echo "Aborting" 217 | # exit 1 218 | # fi 219 | 220 | ############################################################## 221 | # Assemble autopkg-setup.sh options 222 | ############################################################## 223 | 224 | args=() 225 | 226 | if [[ $JSS_URL ]]; then 227 | echo "reading JSS URL" 228 | args+=("--jss-url") 229 | args+=("$JSS_URL") 230 | fi 231 | if [[ $JSS_API_USER ]]; then 232 | echo "reading JSS User" 233 | args+=("--jss-user") 234 | args+=("$JSS_API_USER") 235 | fi 236 | if [[ $JSS_API_PW ]]; then 237 | echo "reading JSS Password" 238 | args+=("--jss-pass") 239 | args+=("$JSS_API_PW") 240 | fi 241 | if [[ $GITHUB_TOKEN ]]; then 242 | echo "reading GitHub Token" 243 | args+=("--github-token") 244 | args+=("$GITHUB_TOKEN") 245 | fi 246 | if [[ $FORCE_AUTOPKG == "true" ]]; then 247 | echo "setting Force AutoPkg reinstallation" 248 | args+=("--force") 249 | fi 250 | if [[ $INSTALL_AUTOPKG_BETA == "true" ]]; then 251 | echo "setting Install AutoPkg beta version" 252 | args+=("--beta") 253 | fi 254 | if [[ $DO_NOT_FAIL_RECIPES_WITHOUT_TRUST_INFO == "true" ]]; then 255 | echo "setting Allow recipes to run without trust information" 256 | args+=("--fail") 257 | fi 258 | if [[ $JAMFUPLOAD_REPO == "true" ]]; then 259 | echo "setting Use jamf-upload repo (beta)" 260 | args+=("--jamf-uploader-repo") 261 | fi 262 | 263 | # now run 264 | progress_dialog >/dev/null 2>&1 265 | "$autopkgsetup" "${args[@]}" 266 | echo "progress: complete" >> "$dialog_log" 267 | echo "quit:" >> "$dialog_log" & sleep 0.1 268 | 269 | # dialog when done 270 | done_dialog 271 | 272 | -------------------------------------------------------------------------------- /autopkg-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # autopkg-setup.sh 4 | # by Graham Pugh 5 | 6 | # autopkg-setup automates the installation of the latest version 7 | # of AutoPkg, optimised for JamfUploader 8 | 9 | # Acknowledgements 10 | # Excerpts from https://github.com/grahampugh/run-munki-run 11 | # which in turn borrows from https://github.com/tbridge/munki-in-a-box 12 | 13 | # -------------------------------------------------------------------------------------- # 14 | # designed to be used with prefs files that autopkg can run with the --prefs option 15 | # These are easy to make with defaults commands or using YAML. 16 | # Store them in a private git account. 17 | # This script will ensure that all repos are added to the server. 18 | # -------------------------------------------------------------------------------------- # 19 | ## No editing required below here 20 | 21 | 22 | rootCheck() { 23 | # Check that the script is NOT running as root 24 | current_user=$(/usr/sbin/scutil <<< "show State:/Users/ConsoleUser" | /usr/bin/awk -F': ' '/[[:space:]]+Name[[:space:]]:/ { if ( $2 != "loginwindow" ) { print $2 }}') 25 | if [[ $EUID -eq 0 ]]; then 26 | echo "### This script is NOT MEANT to run as root." 27 | echo "This script is meant to be run as an admin user." 28 | echo "Please run without sudo." 29 | echo 30 | exit 4 # Running as root. 31 | elif [[ ! -f "${AUTOPKG}" || $force_autopkg_update == "yes" ]]; then 32 | if ! /usr/sbin/dseditgroup -o checkmember -m "$current_user" admin ; then 33 | echo "### This script is meant to be run as an admin user." 34 | echo "Please use a different account or promote this account to admin while this script runs" 35 | echo "(the account can be demoted after AutoPkg is installed)." 36 | echo 37 | exit 5 # Running as a standard user. 38 | fi 39 | fi 40 | } 41 | 42 | installCommandLineTools() { 43 | # Installing the Xcode command line tools on 10.10+ 44 | # This section written by Rich Trouton. 45 | echo "### Installing the command line tools..." 46 | echo 47 | cmd_line_tools_temp_file="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress" 48 | 49 | # convert a macOS major version to a darwin version 50 | os_version=$(sw_vers -productVersion) 51 | if [[ "${os_version:0:2}" == "10" ]]; then 52 | darwin_version=${os_version:3:2} 53 | darwin_version=$((darwin_version+4)) 54 | else 55 | darwin_version=${os_version:0:2} 56 | darwin_version=$((darwin_version+9)) 57 | fi 58 | 59 | # installing the latest Xcode command line tools 60 | 61 | if [[ "$darwin_version" -lt 15 ]] ; then 62 | echo "macOS version $os_version is too old for this script" 63 | exit 1 64 | fi 65 | 66 | echo "macOS version $os_version - proceeding" 67 | 68 | # create the placeholder file which is checked by the softwareupdate tool 69 | # before allowing the installation of the Xcode command line tools. 70 | 71 | touch "$cmd_line_tools_temp_file" 72 | 73 | # identify the correct update in the Software Update feed with "Command Line Tools" in the name 74 | if [[ "$darwin_version" -ge 19 ]] ; then 75 | cmd_line_tools=$(softwareupdate -l | awk '/\*\ Label: Command Line Tools/ { $1=$1;print }' | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 9- | head -n 1) 76 | else 77 | cmd_line_tools=$(softwareupdate -l | awk '/\*\ Command Line Tools/ { $1=$1;print }' | grep "${os_version:3:2}" | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 2-) 78 | fi 79 | 80 | # Iistall the command line tools 81 | if [[ ${cmd_line_tools} ]]; then 82 | echo "Download found - installing" 83 | softwareupdate -i "$cmd_line_tools" --verbose 84 | # 35:43: syntax error: A identifier can’t go after this “"”. (-2740) 85 | # /usr/bin/osascript -e 'do shell script "softwareupdate -i "'"$cmd_line_tools"'" --verbose" with administrator privileges' 86 | else 87 | echo "Download not found" 88 | fi 89 | 90 | # remove the temp file 91 | if [[ -f "$cmd_line_tools_temp_file" ]]; then 92 | rm "$cmd_line_tools_temp_file" 93 | fi 94 | } 95 | 96 | installAutoPkg() { 97 | # Get AutoPkg 98 | # thanks to Nate Felton 99 | # Inputs: 1. $USERHOME 100 | echo "### Downloading AutoPkg installer package..." 101 | echo 102 | if [[ $use_beta == "yes" ]]; then 103 | tag="tags/v3.0.0RC2" 104 | elif [[ $AUTOPKG_TAG ]]; then 105 | tag="tags/$AUTOPKG_TAG" 106 | else 107 | tag="latest" 108 | fi 109 | if [[ $GITHUB_TOKEN ]]; then 110 | # Use the GitHub token if provided 111 | AUTOPKG_PKG=$(curl -sL -H "Accept: application/json" -H "Authorization: Bearer ${GITHUB_TOKEN}" "https://api.github.com/repos/autopkg/autopkg/releases/$tag" | awk -F '"' '/browser_download_url/ { print $4; exit }') 112 | else 113 | # Use the public API if no token is provided 114 | AUTOPKG_PKG=$(curl -sL -H "Accept: application/json" "https://api.github.com/repos/autopkg/autopkg/releases/$tag" | awk -F '"' '/browser_download_url/ { print $4; exit }') 115 | fi 116 | 117 | if ! /usr/bin/curl -L "${AUTOPKG_PKG}" -o "/tmp/autopkg-latest.pkg"; then 118 | echo "### ERROR: could not obtain AutoPkg installer package..." 119 | echo 120 | exit 1 121 | fi 122 | 123 | echo "### Installing AutoPkg..." 124 | echo "ALERT: REQUIRES ADMIN RIGHTS - please enter account password" 125 | echo 126 | echo "### Prompting for administrator credentials to install AutoPkg..." 127 | if [[ $native_prompt == "yes" ]]; then 128 | # Use native prompt if requested 129 | /usr/bin/osascript -e 'do shell script "installer -pkg /tmp/autopkg-latest.pkg -target /" with administrator privileges' 130 | else 131 | # Use sudo prompt 132 | echo "Please enter your administrator password to install AutoPkg." 133 | sudo installer -pkg /tmp/autopkg-latest.pkg -target / 134 | fi 135 | 136 | autopkg_version=$(${AUTOPKG} version) 137 | 138 | ${LOGGER} "AutoPkg $autopkg_version Installed" 139 | echo 140 | echo "### AutoPkg $autopkg_version Installed" 141 | echo 142 | 143 | # Clean Up When Done 144 | # rm "/tmp/autopkg-latest.pkg" 145 | } 146 | 147 | setupPrivateRepo() { 148 | # AutoPkg has no built-in commands for adding private repos as SSH so that you can use a key 149 | # This does the work. Thanks to https://www.johnkitzmiller.com/blog/using-a-private-repository-with-autopkgautopkgr/ 150 | 151 | # clone the recipe repo if it isn't there already 152 | if [[ ! -d "$AUTOPKG_PRIVATE_REPO" ]]; then 153 | ${GIT} clone $AUTOPKG_PRIVATE_REPO_URI "$AUTOPKG_PRIVATE_REPO" 154 | fi 155 | 156 | # add to AutoPkg prefs RECIPE_REPOS 157 | # First check if it's already there - we can leave it alone if so! 158 | if ! ${PLISTBUDDY} -c "Print :RECIPE_REPOS" "${AUTOPKG_PREFS}" &>/dev/null; then 159 | ${PLISTBUDDY} -c "Add :RECIPE_REPOS dict" "${AUTOPKG_PREFS}" 160 | fi 161 | 162 | if ! ${PLISTBUDDY} -c "Print :RECIPE_REPOS:$AUTOPKG_PRIVATE_REPO" "${AUTOPKG_PREFS}" &>/dev/null; then 163 | ${PLISTBUDDY} -c "Add :RECIPE_REPOS:$AUTOPKG_PRIVATE_REPO dict" "${AUTOPKG_PREFS}" 164 | ${PLISTBUDDY} -c "Add :RECIPE_REPOS:$AUTOPKG_PRIVATE_REPO:URL string $AUTOPKG_PRIVATE_REPO_URI" "${AUTOPKG_PREFS}" 165 | fi 166 | 167 | # add to AutoPkg prefs RECIPE_SEARCH_DIRS 168 | if ! ${PLISTBUDDY} -c "Print :RECIPE_SEARCH_DIRS" "${AUTOPKG_PREFS}" &>/dev/null; then 169 | ${PLISTBUDDY} -c "Add :RECIPE_SEARCH_DIRS array" "${AUTOPKG_PREFS}" 170 | fi 171 | # First check if it's already there - we can leave it alone if so! 172 | privateRecipeID=$(${PLISTBUDDY} -c "Print :RECIPE_SEARCH_DIRS" "${AUTOPKG_PREFS}" | grep "$AUTOPKG_PRIVATE_REPO") 173 | if [ -z "$privateRecipeID" ]; then 174 | ${PLISTBUDDY} -c "Add :RECIPE_SEARCH_DIRS: string '$AUTOPKG_PRIVATE_REPO'" "${AUTOPKG_PREFS}" 175 | fi 176 | } 177 | 178 | configureJamfUploader() { 179 | # configure JamfUploader 180 | ${DEFAULTS} write "$AUTOPKG_PREFS" JSS_URL "${JSS_URL}" 181 | 182 | # get API user 183 | if [[ "${JSS_API_USER}" ]]; then 184 | ${DEFAULTS} write "$AUTOPKG_PREFS" API_USERNAME "${JSS_API_USER}" 185 | elif ! ${DEFAULTS} read "$AUTOPKG_PREFS" API_USERNAME &>/dev/null ; then 186 | printf '%s ' "API_USERNAME required. Please enter : " 187 | read -r JSS_API_USER 188 | echo 189 | ${DEFAULTS} write "$AUTOPKG_PREFS" API_USERNAME "${JSS_API_USER}" 190 | fi 191 | 192 | # get API user's password 193 | if [[ "${JSS_API_PW}" == "-" ]]; then 194 | printf '%s ' "API_PASSWORD required. Please enter : " 195 | read -r -s JSS_API_PW 196 | echo 197 | ${DEFAULTS} write "$AUTOPKG_PREFS" API_PASSWORD "${JSS_API_PW}" 198 | elif [[ "${JSS_API_PW}" ]]; then 199 | ${DEFAULTS} write "$AUTOPKG_PREFS" API_PASSWORD "${JSS_API_PW}" 200 | elif ! ${DEFAULTS} read "$AUTOPKG_PREFS" API_PASSWORD &>/dev/null ; then 201 | printf '%s ' "API_PASSWORD required. Please enter : " 202 | read -r -s JSS_API_PW 203 | echo 204 | ${DEFAULTS} write "$AUTOPKG_PREFS" API_PASSWORD "${JSS_API_PW}" 205 | fi 206 | 207 | # JamfUploader requires simple defaults keys for the repo 208 | if [[ "${SMB_URL}" ]]; then 209 | ${DEFAULTS} write "$AUTOPKG_PREFS" SMB_URL "${SMB_URL}" 210 | 211 | # get SMB user's password 212 | if [[ "${SMB_USERNAME}" ]]; then 213 | ${DEFAULTS} write "$AUTOPKG_PREFS" SMB_USERNAME "${SMB_USERNAME}" 214 | elif ! ${DEFAULTS} read "$AUTOPKG_PREFS" SMB_USERNAME &>/dev/null ; then 215 | printf '%s ' "SMB_USERNAME required. Please enter : " 216 | read -r SMB_USERNAME 217 | echo 218 | ${DEFAULTS} write "$AUTOPKG_PREFS" SMB_USERNAME "${SMB_USERNAME}" 219 | fi 220 | 221 | # get SMB user's password 222 | if [[ "${SMB_PASSWORD}" == "-" ]]; then 223 | printf '%s ' "SMB_PASSWORD required. Please enter : " 224 | read -r -s SMB_PASSWORD 225 | echo 226 | ${DEFAULTS} write "$AUTOPKG_PREFS" SMB_PASSWORD "${SMB_PASSWORD}" 227 | elif [[ "${SMB_PASSWORD}" ]]; then 228 | ${DEFAULTS} write "$AUTOPKG_PREFS" SMB_PASSWORD "${SMB_PASSWORD}" 229 | elif ! ${DEFAULTS} read "$AUTOPKG_PREFS" SMB_PASSWORD &>/dev/null ; then 230 | printf '%s ' "SMB_PASSWORD required. Please enter : " 231 | read -r -s SMB_PASSWORD 232 | echo 233 | ${DEFAULTS} write "$AUTOPKG_PREFS" SMB_PASSWORD "${SMB_PASSWORD}" 234 | fi 235 | fi 236 | } 237 | 238 | configureSlack() { 239 | # get Slack user and webhook 240 | if [[ "${SLACK_USERNAME}" ]]; then 241 | ${DEFAULTS} write "$AUTOPKG_PREFS" SLACK_USERNAME "${SLACK_USERNAME}" 242 | echo "### Wrote SLACK_USERNAME $SLACK_USERNAME to $AUTOPKG_PREFS" 243 | fi 244 | if [[ "${SLACK_WEBHOOK}" ]]; then 245 | ${DEFAULTS} write "$AUTOPKG_PREFS" SLACK_WEBHOOK "${SLACK_WEBHOOK}" 246 | echo "### Wrote SLACK_WEBHOOK $SLACK_WEBHOOK to $AUTOPKG_PREFS" 247 | fi 248 | } 249 | 250 | 251 | ## Main section 252 | 253 | # Commands 254 | GIT="/usr/bin/git" 255 | DEFAULTS="/usr/bin/defaults" 256 | AUTOPKG="/usr/local/bin/autopkg" 257 | PLISTBUDDY="/usr/libexec/PlistBuddy" 258 | 259 | # logger 260 | LOGGER="/usr/bin/logger -t AutoPkg_Setup" 261 | 262 | # declare array for recipe lists 263 | AUTOPKG_RECIPE_LISTS=() 264 | 265 | # default for failing unverified recipes 266 | fail_recipes="yes" 267 | 268 | # get arguments 269 | while test $# -gt 0 270 | do 271 | case "$1" in 272 | -f|--force) force_autopkg_update="yes" 273 | ;; 274 | --prompt) native_prompt="yes" 275 | ;; 276 | -b|--beta) 277 | force_autopkg_update="yes" 278 | use_beta="yes" 279 | ;; 280 | -t|--tag) 281 | shift 282 | AUTOPKG_TAG="$1" 283 | if [[ ! $AUTOPKG_TAG =~ ^v[0-9]+ ]]; then 284 | echo "### ERROR: Tag must start with 'v' and be followed by a version number." 285 | exit 1 286 | fi 287 | ;; 288 | -x|--fail) 289 | fail_recipes="no" 290 | ;; 291 | --prefs) 292 | shift 293 | AUTOPKG_PREFS="$1" 294 | [[ $AUTOPKG_PREFS == "/"* ]] || AUTOPKG_PREFS="$(pwd)/${AUTOPKG_PREFS}" 295 | [[ $AUTOPKG_PREFS != *".plist" ]] && AUTOPKG_PREFS="${AUTOPKG_PREFS}.plist" 296 | echo "AUTOPKG_PREFS : $AUTOPKG_PREFS" 297 | ;; 298 | --replace-prefs) replace_prefs="yes" 299 | ;; 300 | --github-token) 301 | shift 302 | GITHUB_TOKEN="$1" 303 | ;; 304 | --gh-token-file) 305 | shift 306 | GITHUB_TOKEN_FILE="$1" 307 | if [[ -f "$GITHUB_TOKEN_FILE" ]]; then 308 | GITHUB_TOKEN=$(cat "$GITHUB_TOKEN_FILE") 309 | else 310 | echo "### ERROR: GitHub token file not found: $GITHUB_TOKEN_FILE" 311 | exit 1 312 | fi 313 | ;; 314 | --private-repo-url) 315 | shift 316 | AUTOPKG_PRIVATE_REPO_URI="$1" 317 | ;; 318 | --recipe-list) 319 | shift 320 | AUTOPKG_RECIPE_LISTS+=("$1") 321 | ;; 322 | --repo-list) 323 | shift 324 | AUTOPKG_REPO_LIST="$1" 325 | ;; 326 | --jss-url) 327 | shift 328 | JSS_URL="$1" 329 | ;; 330 | --jss-user) 331 | shift 332 | JSS_API_USER="$1" 333 | ;; 334 | --jss-pass) 335 | shift 336 | JSS_API_PW="$1" 337 | ;; 338 | --slack-webhook) 339 | shift 340 | SLACK_WEBHOOK="$1" 341 | ;; 342 | --slack-user) 343 | shift 344 | SLACK_USERNAME="$1" 345 | ;; 346 | --smb-url) 347 | shift 348 | SMB_URL="$1" 349 | ;; 350 | --smb-user) 351 | shift 352 | SMB_USERNAME="$1" 353 | ;; 354 | --smb-pass) 355 | shift 356 | SMB_PASSWORD="$1" 357 | ;; 358 | --jamf-uploader-repo) 359 | jamf_upload_repo="yes" 360 | ;; 361 | *) 362 | echo " 363 | Usage: 364 | ./autopkg_setup.sh [options] 365 | 366 | By default, this script will install the latest version of AutoPkg (v2.7.5 at the time of writing). 367 | 368 | Options: 369 | 370 | -h | --help Displays this text 371 | -f | --force Force the re-installation of the latest AutoPkg 372 | -b | --beta Force the installation of the pre-released version of AutoPkg 373 | (currently v3.0.0RC2) 374 | -t | --tag * Force the installation of a specific tag version of AutoPkg 375 | (e.g. v2.7.4) 376 | Must start with 'v' and be followed by a version number. 377 | -x | --fail Don't fail autopkg recipe runs if not verified 378 | -j | --jcds2-mode Set to jcds2_mode 379 | -p | --prefs * Path to the preferences plist 380 | (default is /Library/Preferences/com.github.autopkg.plist) 381 | 382 | --replace-prefs Delete the prefs file and rebuild from scratch 383 | --github-token * A GitHub token - required to prevent hitting API limits 384 | --gh-token-file * Path to a text file containing a GitHub token 385 | (e.g. /Users/Shared/gh_token) 386 | 387 | --private-repo * Path to a private repo 388 | --private-repo-url * The private repo url 389 | 390 | --repo-list * Path to a repo-list file. All repos will be added to the prefs file. 391 | 392 | --recipe-list * Path to a recipe list. If this method is used, all parent repos 393 | are added, but the recipes must be in a repo that is already installed. 394 | 395 | JamfUploader settings: 396 | 397 | --jss-url * URL of the Jamf server 398 | --jss-user * API account username 399 | --jss-pass * API account password 400 | 401 | --smb-url * URL of the FileShare Distribution Point 402 | --smb-user * Username of account that has access to the DP 403 | --smb-pass * Password of account that has access to the DP 404 | 405 | --jamf-uploader-repo Use the grahampugh/jamf-upload repo instead of autopkg/grahampugh-recipes 406 | for JamfUploader processors (effectively JamfUploader beta access) 407 | 408 | Slack settings: 409 | 410 | --slack-webhook * Slack webhook 411 | --slack-user * A display name for the Slack notifications 412 | 413 | " 414 | exit 0 415 | ;; 416 | esac 417 | shift 418 | done 419 | 420 | # root check 421 | rootCheck 422 | 423 | # Check for Command line tools. 424 | if ! xcode-select -p >/dev/null 2>&1 ; then 425 | echo "Xcode Command Line Tools not found, proceeding to install." 426 | installCommandLineTools 427 | else 428 | echo "Xcode Command Line Tools found." 429 | fi 430 | 431 | # check CLI tools are functional 432 | if ! $GIT --version >/dev/null 2>&1 ; then 433 | echo "Xcode Command Line Tools not functional, proceeding to install." 434 | installCommandLineTools 435 | else 436 | echo "Xcode Command Line Tools functional." 437 | fi 438 | 439 | # double-check CLI tools are functional 440 | if ! $GIT --version >/dev/null 2>&1 ; then 441 | $LOGGER "ERROR: Xcode Command Line Tools failed to install" 442 | echo 443 | echo "### ERROR: Xcode Command Line Tools failed to install." 444 | exit 1 445 | else 446 | $LOGGER "Xcode Command Line Tools installed and functional" 447 | echo 448 | echo "### Xcode Command Line Tools installed and functional" 449 | fi 450 | 451 | # Get AutoPkg if not already installed 452 | if [[ ! -f "${AUTOPKG}" || $force_autopkg_update == "yes" ]]; then 453 | 454 | installAutoPkg "${HOME}" 455 | ${LOGGER} "AutoPkg installed and secured" 456 | echo 457 | echo "### AutoPkg installed and secured" 458 | fi 459 | 460 | # read the supplied prefs file or else use the default 461 | if [[ ! $AUTOPKG_PREFS ]]; then 462 | AUTOPKG_PREFS="$HOME/Library/Preferences/com.github.autopkg.plist" 463 | fi 464 | 465 | # kill any existing prefs 466 | if [[ $replace_prefs ]]; then 467 | rm -f "$AUTOPKG_PREFS" ||: 468 | fi 469 | 470 | # add the GIT path to the prefs 471 | ${DEFAULTS} write "${AUTOPKG_PREFS}" GIT_PATH "$(which git)" 472 | echo "### Wrote GIT_PATH $(which git) to $AUTOPKG_PREFS" 473 | 474 | # add the GitHub token to the prefs 475 | if [[ $GITHUB_TOKEN ]]; then 476 | GITHUB_TOKEN_PATH="$HOME/Library/AutoPkg/gh_token" 477 | echo "$GITHUB_TOKEN" > "$GITHUB_TOKEN_PATH" 478 | echo "### Wrote GITHUB_TOKEN to $GITHUB_TOKEN_PATH" 479 | ${DEFAULTS} write "${AUTOPKG_PREFS}" GITHUB_TOKEN_PATH "$GITHUB_TOKEN_PATH" 480 | echo "### Wrote GITHUB_TOKEN_PATH to $AUTOPKG_PREFS" 481 | fi 482 | 483 | # ensure untrusted recipes fail 484 | if [[ $fail_recipes == "no" ]]; then 485 | ${DEFAULTS} write "$AUTOPKG_PREFS" FAIL_RECIPES_WITHOUT_TRUST_INFO -bool false 486 | echo "### Wrote FAIL_RECIPES_WITHOUT_TRUST_INFO false to $AUTOPKG_PREFS" 487 | else 488 | ${DEFAULTS} write "$AUTOPKG_PREFS" FAIL_RECIPES_WITHOUT_TRUST_INFO -bool true 489 | echo "### Wrote FAIL_RECIPES_WITHOUT_TRUST_INFO true to $AUTOPKG_PREFS" 490 | fi 491 | 492 | # set jcds2_mode 493 | if [[ $jcds2_mode == "yes" ]]; then 494 | ${DEFAULTS} write "$AUTOPKG_PREFS" jcds2_mode -bool true 495 | echo "### Wrote jcds2_mode true to $AUTOPKG_PREFS" 496 | elif ${DEFAULTS} read com.github.autopkg jcds2_mode 2>/dev/null; then 497 | ${DEFAULTS} delete "$AUTOPKG_PREFS" jcds2_mode 498 | fi 499 | 500 | # add Slack credentials if anything supplied 501 | if [[ $SLACK_USERNAME || $SLACK_WEBHOOK ]]; then 502 | configureSlack 503 | fi 504 | 505 | # ensure we have the recipe list dictionary and array 506 | if ! ${DEFAULTS} read "$AUTOPKG_PREFS" RECIPE_SEARCH_DIRS 2>/dev/null; then 507 | ${DEFAULTS} write "$AUTOPKG_PREFS" RECIPE_SEARCH_DIRS -array 508 | fi 509 | if ! ${DEFAULTS} read "$AUTOPKG_PREFS" RECIPE_REPOS 2>/dev/null; then 510 | ${DEFAULTS} write "$AUTOPKG_PREFS" RECIPE_REPOS -dict 511 | fi 512 | 513 | # build the repo list 514 | AUTOPKG_REPOS=() 515 | 516 | # If using the jamf-upload repo, we have to make sure it's above grahampugh-recipes in the search 517 | if [[ "$jamf_upload_repo" == "yes" ]]; then 518 | if autopkg list-repos --prefs "$AUTOPKG_PREFS" | grep grahampugh-recipes; then 519 | ${AUTOPKG} repo-delete grahampugh-recipes --prefs "$AUTOPKG_PREFS" 520 | fi 521 | AUTOPKG_REPOS+=("grahampugh/jamf-upload") 522 | else 523 | if autopkg list-repos --prefs "$AUTOPKG_PREFS" | grep grahampugh/jamf-upload; then 524 | ${AUTOPKG} repo-delete grahampugh/jamf-upload --prefs "$AUTOPKG_PREFS" 525 | fi 526 | fi 527 | 528 | # always add grahampugh-recipes 529 | AUTOPKG_REPOS+=("grahampugh-recipes") 530 | 531 | # add more if there is a repo-list supplied 532 | if [[ -f "$AUTOPKG_REPO_LIST" ]]; then 533 | while IFS= read -r; do 534 | repo="$REPLY" 535 | AUTOPKG_REPOS+=("$repo") 536 | done < "$AUTOPKG_REPO_LIST" 537 | fi 538 | 539 | # Add AutoPkg repos (checks if already added) 540 | for r in "${AUTOPKG_REPOS[@]}"; do 541 | if ${AUTOPKG} repo-add "$r" --prefs "$AUTOPKG_PREFS" 2>/dev/null; then 542 | echo "Added $r to $AUTOPKG_PREFS" 543 | else 544 | echo "ERROR: could not add $r to $AUTOPKG_PREFS" 545 | fi 546 | done 547 | 548 | ${LOGGER} "AutoPkg Repos Configured" 549 | echo 550 | echo "### AutoPkg Repos Configured" 551 | 552 | # Add private repo if set 553 | if [[ $AUTOPKG_PRIVATE_REPO && $AUTOPKG_PRIVATE_REPO_URI ]]; then 554 | setupPrivateRepo 555 | ${LOGGER} "Private AutoPkg Repo Configured" 556 | echo 557 | echo "### Private AutoPkg Repo Configured" 558 | fi 559 | 560 | if [[ $JSS_URL ]]; then 561 | # Configure JamfUploader 562 | configureJamfUploader 563 | ${LOGGER} "JamfUploader configured." 564 | echo 565 | echo "### JamfUploader configured." 566 | fi 567 | 568 | # ensure all repos associated with an inputted recipe list(s) are added 569 | if [ ${#AUTOPKG_RECIPE_LISTS[@]} -ne 0 ]; then 570 | for file in "${AUTOPKG_RECIPE_LISTS[@]}"; do 571 | while read -r recipe ; do 572 | ${AUTOPKG} info -p "${recipe}" --prefs "$AUTOPKG_PREFS" 573 | done < "$file" 574 | done 575 | 576 | ${LOGGER} "AutoPkg Repos for all parent recipes added" 577 | echo 578 | echo "### AutoPkg Repos for all parent recipes added" 579 | fi 580 | 581 | --------------------------------------------------------------------------------