Merge pull request #406 from Installomator/2022-02-02_Theile-base

Version 9.0 fixed, from branch 2022 02 02 theile base
This commit is contained in:
Armin Briegel
2022-02-08 10:10:40 +01:00
committed by GitHub
30 changed files with 1057 additions and 341 deletions

View File

@@ -1,11 +1,29 @@
## v9? ## v9?
- We have moved the root check to the beginning of the script, and improved DEBUG handling with two different modes. `DEBUG=0` is still for production, and `1` is still for the DEBUG we previously knew downloading to the directory it is running from, but `2` will download to temporary folder, will detect updates, but will not install anything, but it will notify the user (almost as running the script without root before). - We have moved the root check to the beginning of the script, and improved DEBUG handling with two different modes. `DEBUG=0` is still for production, and `1` is still for the DEBUG we previously knew downloading to the directory it is running from, but `2` will download to temporary folder, will detect updates, but will not install anything, but it will notify the user (almost as running the script without root before).
- Added option to not interrupt Do Not Disturb full screen apps like Keynote or Zoom with `INTERRUPT_DND="no"`. Default is `"yes"` which is how it has worked until now.
- `pkgName` in a label can now be searched for. An example is logitechoptions, where only the name of the pkg is given, and not the exact file path to it. - `pkgName` in a label can now be searched for. An example is logitechoptions, where only the name of the pkg is given, and not the exact file path to it.
- `LSMinimumSystemVersion` will now be honered, if the `Info.plist` in the app is specifying this. That means that an app that has this parameter in that file and it shows that the app requires a newer version of the OS than is currently installed, then we will not install it. - `LSMinimumSystemVersion` will now be honered, if the `Info.plist` in the app is specifying this. That means that an app that has this parameter in that file and it shows that the app requires a newer version of the OS than is currently installed, then we will not install it.
- New variable `RETURN_LABEL_NAME`. If given the value `1`, like `RETURN_LABEL_NAME=1` then Installomator only returns the name of the label. It makes for a better user friendly message for displaying in DEPNotify if that is integrated. - New variable `RETURN_LABEL_NAME`. If given the value `1`, like `RETURN_LABEL_NAME=1` then Installomator only returns the name of the label. It makes for a better user friendly message for displaying in DEPNotify if that is integrated.
- Changed logic if `IGNORE_APP_STORE_APPS=yes`. Before this version a label like `microsoftonedrive` that was installed from App Store, and that we want to replace with the “ordinary” version, Installomator would still use `updateTool`, even though `IGNORE_APP_STORE_APPS=yes`. So we would have to have `INSTALL=force` in order to have the app replaced, as `updateTool` would be used. But now if `IGNORE_APP_STORE_APPS=yes` then `updateTool` will be not set, and the App Store app will be replaced. BUT if the installed software was not from App Store, then `updateTool` will not be used, and it would be a kind of a forced install (in the example of `microsoftonedrive`), except if the version is the same (where installation is skipped). - Changed logic if `IGNORE_APP_STORE_APPS=yes`. Before this version a label like `microsoftonedrive` that was installed from App Store, and that we want to replace with the “ordinary” version, Installomator would still use `updateTool`, even though `IGNORE_APP_STORE_APPS=yes`. So we would have to have `INSTALL=force` in order to have the app replaced, as `updateTool` would be used. But now if `IGNORE_APP_STORE_APPS=yes` then `updateTool` will be not set, and the App Store app will be replaced. BUT if the installed software was not from App Store, then `updateTool` will not be used, and it would be a kind of a forced install (in the example of `microsoftonedrive`), except if the version is the same (where installation is skipped).
- Added variable `SYSTEMOWNER` that is used when copying files when installing. Default `0` is to change owner of the app to the current user on the Mac, like this user was installing this app themselves. When using `1` we will put “root:wheel” on the app, which can be useful for shared machines. - Added variable `SYSTEMOWNER` that is used when copying files when installing. Default `0` is to change owner of the app to the current user on the Mac, like this user was installing this app themselves. When using `1` we will put “root:wheel” on the app, which can be useful for shared machines.
- Added option `curlOptions` to the labels. It can be filled with extra headers need for downloading the specific software. It needs to be an array, like `curlOptions=( )`. See “mocha”-software-labels.
Big changes to logging:
- Introducing variable `LOGGING`, that can be either of the logging levels
- Logging levels:
0: DEBUG Everything is logged
1: INFO Normal logging behavior
2: WARN
3: ERROR
4: REQ
- External logging to Datadog
- A function to shorten duplicate lines in installation logs or output of longer commands
- Ability to extract install.log in the time when Installomator was running, if further investigations needs to be done to logs
Fixes:
- Fixed a problem with pkgs: If they were mounted with .pkg in the name, then we would find the directory and not the pkg file itself.
- Minor fix for a check for a pkgName on a DMG. We used `ls` that would throw an error when not found, so the check was corrected.
## v8.0 ## v8.0

View File

@@ -22,6 +22,7 @@ anydesk
apparency apparency
appcleaner appcleaner
applenyfonts applenyfonts
applesfarabic
applesfcompact applesfcompact
applesfmono applesfmono
applesfpro applesfpro
@@ -72,6 +73,7 @@ cormorant
craftmanager craftmanager
cryptomator cryptomator
cyberduck cyberduck
daisydisk
dangerzone dangerzone
darktable darktable
dbeaverce dbeaverce
@@ -84,6 +86,7 @@ devonthink
dialog dialog
dialpad dialpad
discord discord
diskspace
docker docker
drift drift
dropbox dropbox
@@ -110,6 +113,7 @@ firefoxesr_intl
firefoxesrpkg firefoxesrpkg
firefoxpkg firefoxpkg
flowjo flowjo
flux
front front
fsmonitor fsmonitor
gimp gimp
@@ -136,7 +140,9 @@ hazel
hpeasyadmin hpeasyadmin
hpeasystart hpeasystart
hyper hyper
ibarcoder
icons icons
iina
imazingprofileeditor imazingprofileeditor
inkscape inkscape
insomnia insomnia
@@ -145,6 +151,7 @@ installomator_theile
intellijideace intellijideace
istatmenus istatmenus
iterm2 iterm2
itsycal
jabradirect jabradirect
jamfconnect jamfconnect
jamfconnectconfiguration jamfconnectconfiguration
@@ -177,6 +184,7 @@ libreoffice
logitechoptions logitechoptions
logseq logseq
loom loom
lowprofile
lucifer lucifer
lulu lulu
maccyapp maccyapp
@@ -211,6 +219,12 @@ microsoftvisualstudiocode
microsoftword microsoftword
microsoftyammer microsoftyammer
miro miro
mobikinassistantforandroid
mochakeyboard
mochatelnet
mochatn3270
mochatn3812
mochatn5250
montereyblocker montereyblocker
mowgliiitsycal mowgliiitsycal
musescore musescore
@@ -242,6 +256,7 @@ ottomatic
overflow overflow
pacifist pacifist
pandoc pandoc
paretosecurity
parsec parsec
pdfsam pdfsam
perimeter81 perimeter81
@@ -251,16 +266,22 @@ platypus
plisteditpro plisteditpro
postman postman
prism9 prism9
pritunl
privileges privileges
proctortrack proctortrack
promiseutility
promiseutilityr promiseutilityr
protonvpn
proxyman proxyman
pycharmce pycharmce
pymol pymol
r r
ramboxce ramboxce
rancherdesktop
rectangle rectangle
redeye redeye
remotedesktopmanagerenterprise
remotedesktopmanagerfree
remotix remotix
remotixagent remotixagent
resiliosynchome resiliosynchome
@@ -280,6 +301,8 @@ scaleft
screamingfrogseospider screamingfrogseospider
screencloudplayer screencloudplayer
screenflick screenflick
sdnotary
secretive
sequelpro sequelpro
sfsymbols sfsymbols
shield shield
@@ -314,6 +337,7 @@ suspiciouspackage
swiftruntimeforcommandlinetools swiftruntimeforcommandlinetools
sync sync
tableaudesktop tableaudesktop
tableaupublic
tableaureader tableaureader
tageditor tageditor
talkdeskcallbar talkdeskcallbar
@@ -343,7 +367,7 @@ utm
vagrant vagrant
vanilla vanilla
veracrypt veracrypt
virtualbox vimac
viscosity viscosity
visualstudiocode visualstudiocode
vivaldi vivaldi
@@ -376,4 +400,5 @@ zoomrooms
zulujdk11 zulujdk11
zulujdk13 zulujdk13
zulujdk15 zulujdk15
zulujdk17
zulujdk8 zulujdk8

176
README.md
View File

@@ -8,11 +8,23 @@ This script is in the “we find it useful, it is working for us” stage.
Your production and deployment environment will be different, please test thoroughly before rolling it out to your production. Your production and deployment environment will be different, please test thoroughly before rolling it out to your production.
I have put a lot of work into making it stable and safe, but I cannot - of course - make _any_ promises that it won't break in some not yet encountered edge case. We have put a lot of work into making it stable and safe, but we cannot - of course - make _any_ promises that it won't break in some not yet encountered edge case.
## Authors
Intallomator was original inspired by the download scripts from William Smith and Sander Schram, and created by:
Armin Briegel - @scriptingosx
Later on a few more contributers came on the project:
Isaac Ordonez - @issacatmann
Søren Theilgaard - @Theile
Adam Codega - @acodega
And with numerous contributions from many others.
## Support and Contributing ## Support and Contributing
__Please note, that if you are contributing to this project with new labels or other suggestions in PRs, please put your changes in the files below `fragments`-folder. DO NOT edit the full `Installomator.sh` script. The full script is now a build of the fragments, and will be overwritten. See the REAMDME.md file in the `utils` directory for detailed instructions.__ __Please note, that if you are contributing to this project with new labels or other suggestions in PRs, please put your changes in the files below `fragments`-folder. DO NOT edit the full `Installomator.sh` script. The full script is now a build of the fragments, and will be overwritten. See the [README.md](utils/README.md) file in the `utils` directory for detailed instructions.__
Discussion, support and advice around Installomator happens in the `#installomator` channel in the [MacAdmins.org Slack](https://macadmins.org). Go there for support questions. Discussion, support and advice around Installomator happens in the `#installomator` channel in the [MacAdmins.org Slack](https://macadmins.org). Go there for support questions.
@@ -22,30 +34,43 @@ Please see [CONTRIBUTING.md](https://github.com/Installomator/Installomator/blob
## More reading ## More reading
There are a few interesting post on Installomator on my weblog: Our wiki:
- [Wiki](https://github.com/Installomator/Installomator/wiki)
There are a few interesting post on Installomator on Armins weblog:
- [Introducing Installomator](https://scriptingosx.com/2020/05/introducing-installomator/) - [Introducing Installomator](https://scriptingosx.com/2020/05/introducing-installomator/)
- [Using Installomator with Jamf Pro](https://scriptingosx.com/2020/06/using-installomator-with-jamf-pro/) by Mischa van der Bent - [Using Installomator with Jamf Pro](https://scriptingosx.com/2020/06/using-installomator-with-jamf-pro/) by Mischa van der Bent
## Background ## Background
As a system engineer at [an Apple Authorized Enterprise Reseller](https://prowarehouse.nl), we manage a lot of Jamf instances. In the world of managing Apple Macs, organizations can have two different approaches to the management. Either the IT department will tightly manage and verify each piece of software, or they will just want the latest software to be deployed as fast as possible.
Some of these instances are tightly managed, i.e. the versions of the operating system and third party software are controlled and updates will only be pushed with the management system when the administration and security team went through an approval process and then the update is automated. This is an important and valid workflow and the right fit for many deployments. OK, maybe some software should be tightly managed and others not, but you get the point.
### Tightly managed
If your solution needs to be tightly managed, i.e. the versions of the operating system and third party software are controlled and updates will only be pushed with the management system when the administration and security team went through an approval process and then the update is automated. This is an important and valid workflow and the right fit for many deployments.
Installomator was _not_ written for these kinds of deployment. Installomator was _not_ written for these kinds of deployment.
If you are running this kind of deployment, you want to use [AutoPkg](https://github.com/autopkg/autopkg) and you can stop reading here. If you are running this kind of deployment, you want to use [AutoPkg](https://github.com/autopkg/autopkg) and you can stop reading here.
There are other kinds of deployments, though. In these deployments the management system is merely used to "get the user ready" as quickly as possible when they set up a new machine, and to offer software from a self service portal. In these deployments, system and software installations are 'latest version available' and updates are user driven (though we do want to nag them). ### Latest version always
These deployments are There are other kinds of deployments, though. In these deployments the management system is merely used to “get the user ready” as quickly as possible when they set up a new machine, and to offer software from a self service portal. In these deployments, system and software installations are latest version available and updates are user driven (though we do want to nag them and push them).
Installomator can help with this.
These deployments can be
- user driven - user driven
- low control - low control
- minimal maintenance effort - minimal maintenance effort
- latest version is best
These are mostly 'user controlled' Macs and we (the admins) just want to assist the user in doing the right thing. And the right thing is (often) to install the latest versions and updates when they are available. These can be 'user controlled' Macs and we (the admins) just want to assist the user in doing the right thing. And the right thing is (often) to install the latest versions and updates when they are available.
The Mac App Store and software pushed through the Mac App Store follow this approach. When you manage and deploy software through the App Store — whether it is on iOS or macOS — neither the MacAdmin nor the user get a choice of the application version. They will get the latest version. The Mac App Store and software pushed through the Mac App Store follow this approach. When you manage and deploy software through the App Store — whether it is on iOS or macOS — neither the MacAdmin nor the user get a choice of the application version. They will get the latest version.
@@ -62,16 +87,25 @@ Some of these disadvantages can be seen as advantages in different setups. When
Because this is an attractive solution for _certain kinds_ of deployment, there are already many scripts out there that will download and install the latest version of a given software. And we have built and used quite a few in-house, as well. Most importantly, [William Smith has this script](https://gist.github.com/talkingmoose/a16ca849416ce5ce89316bacd75fc91a) which can be used to install several different Microsoft applications and bundles, because Microsoft has a nice unified URL scheme. Because this is an attractive solution for _certain kinds_ of deployment, there are already many scripts out there that will download and install the latest version of a given software. And we have built and used quite a few in-house, as well. Most importantly, [William Smith has this script](https://gist.github.com/talkingmoose/a16ca849416ce5ce89316bacd75fc91a) which can be used to install several different Microsoft applications and bundles, because Microsoft has a nice unified URL scheme.
At some point, earlier this year, I got frustrated at the number of scripts we were maintaining (or failing to). Also, my concern that most of the scripts weren't doing _any_ verification of the download was getting unbearable. So, I set out to write the one install script to rule them all... At some point, in 2018, Armin got frustrated at the number of scripts he was maintaining (or failing to). Also, his concern that most of the scripts werent doing _any_ verification of the download was getting unbearable. So, he set out to write _the one install script to rule them all_
### Locally installed
So Armin made the version for Jamf Pro but universally for any MDM to adopt.
Søren looked at this, and wanted this approach to work in Mosyle and Addigy, and for these solutions we need Installomator to be locally installed on. the Mac, and then the MDM can call this script from their scripts features. For some time Søren had a version of Installomator that was supplied with a notarized pkg, so it could be deployed as part of DEP or however was needed.
This has now been merged into Installomator, and with contributions of Isaac and Adam, new features and labels have been added more frequently.
## Goals ## Goals
My goals for Installomator are: The goals for Installomator are:
- work with various common archive types - work with various common archive types
- verify the downloaded archive or application - verify the downloaded archive or application
- have a simple 'interface' to the admin - have a simple interface to the admin
- single script file so it can 'easily' be copied into a management system - single script file so it can easily be copied into a management system
- signed and notarized pkg-installer for local installation
- extensible without deep scripting knowledge - extensible without deep scripting knowledge
- work independently of a specific management system - work independently of a specific management system
- no dependencies that may be removed from macOS in the future or are not pre-installed - no dependencies that may be removed from macOS in the future or are not pre-installed
@@ -88,9 +122,9 @@ Installomator can work with the following common archive and installer types:
When the download yields a pkg file, Installomator will run `installer` to install it on the current system. When the download yields a pkg file, Installomator will run `installer` to install it on the current system.
Applications in dmgs or zips will be copied to `/Applications` and their owner will be set to the current user, so the install works like a standard drag'n drop installation. Applications in dmgs or zips will be copied to `/Applications` and their owner will be set to the current user, so the install works like a standard drag'n drop installation. Owner can also be set to root/wheel.
(I consider it a disgrace, that Jamf, after nearly 20 years, _still_ cannot deal with 'drag'n drop installation dmgs' natively. It's not _that_ hard.) (I consider it a disgrace, that Jamf, after nearly 20 years, _still_ cannot deal with dragn drop installation dmgs natively. Its not _that_ hard.)
### Verify the download ### Verify the download
@@ -107,7 +141,7 @@ When used to install software, Installomator has a single argument: the label or
./Installomator.sh firefox LOGO=jamf BLOCKING_PROCESS_ACTION=tell_user_then_kill NOTIFY=all ./Installomator.sh firefox LOGO=jamf BLOCKING_PROCESS_ACTION=tell_user_then_kill NOTIFY=all
``` ```
There is a debug mode and one other setting that can be controlled with variables in the code. This simplifies the actual use of the script from within a management system. There is a debug mode and other settings that can be controlled with variables in the code. This simplifies the actual use of the script from within a management system.
### Extensible ### Extensible
@@ -132,19 +166,21 @@ googlechrome)
When you know how to extract these pieces of information from the application and/or download, then you can add an application to Installomator. When you know how to extract these pieces of information from the application and/or download, then you can add an application to Installomator.
The script `buildCaseStatement.sh` can help with the label creation. The script `buildLabel.sh` can help with the label creation. Just server the URL to the script, and it will try to figure out things and write out a label as output. See [Wiki Tutorials](https://github.com/Installomator/Installomator/wiki#tutorials).
Please note: Labels should be named in small caps, numbers 0-9, “-”, and “_”. No other characters allowed. Please note: Labels should be named in small caps, numbers 0-9, “-”, and “_”. No other characters allowed.
Actually labels are part of a case-statement, and must be formatted accordingly.
### Not specific to a management system ### Not specific to a management system
I wrote this script mainly for use with Jamf Pro, because that is what we use. For testing, you can run the script interactively from the command line. However, I have tried to keep anything that is specific to Jamf optional, or so flexible that it will work anywhere. Even if it does not work with your management system 'out of the box,' the adaptations should be straightforward. Armin wrote this script mainly for use with Jamf Pro, because that is what he used. For testing, you can run the script interactively from the command line. However, we have tried to keep anything that is specific to Jamf optional, or so flexible that it will work anywhere. Even if it does not work with your management system out of the box, the adaptations should be straightforward.
Not all MDMs can include the full script, for those MDMs it might be more useful to install it on the client machines, and run it from there. So a PKG to be installed on client Macs is also provided here. Not all MDMs can include the full script, for those MDMs it might be more useful to install it on the client machines, and run it from there. So a PKG to be installed on client Macs is also provided here.
### No dependencies ### No dependencies
The script started out as a pure `sh` script, and when I needed arrays I 'switched' to `zsh`, because that is what [we can rely on being in macOS for the foreseeable future](https://scriptingosx.com/zsh). There are quite a few places where using python would have been easier and safer, but with the python 2 run-time being deprecated, that would have added a requirement for a Python 3 run-time to be installed. XML and JSON parsing would have been better with a tool like [scout](https://github.com/ABridoux/scout) or [jq](https://stedolan.github.io/jq/), but those would again require additional installations on the client before the script can run. The script started out as a pure `sh` script, and when arrays was needed it was switched to `zsh`, because that is what [we can rely on being in macOS for the foreseeable future](https://scriptingosx.com/zsh). There are quite a few places where using python would have been easier and safer, but with the python 2 run-time being deprecated, that would have added a requirement for a Python 3 run-time to be installed. XML and JSON parsing would have been better with a tool like [scout](https://github.com/ABridoux/scout) or [jq](https://stedolan.github.io/jq/), but those would again require additional installations on the client before the script can run.
Keeping the script as a `zsh` allows you to paste it into your management system's interface (and disable the DEBUG mode) and use it without requiring any other installations. Keeping the script as a `zsh` allows you to paste it into your management system's interface (and disable the DEBUG mode) and use it without requiring any other installations.
@@ -177,19 +213,19 @@ Other than the version arguments, the argument can be any of the labels listed i
### Debug mode ### Debug mode
There is a variable named `DEBUG` which is set in line 21 of the script. When `DEBUG` is set to `1` (default) no actions that would actually modify the current system are taken. This is useful for testing most of the actions in the script, but obviously not all of them. There is a variable named `DEBUG` which is set in line 21 of the script. When `DEBUG` is set to `1` (default) or `2` for a variation of debug, no actions that would actually modify the current system are taken. This is useful for testing most of the actions in the script, but obviously not all of them.
Also when the `DEBUG` variable is `1`, downloaded archives and extracted files will be written to the script's directory, rather than a temporary directory, which can make debugging easier. When the `DEBUG` variable is `1`, downloaded archives and extracted files will be written to the script's directory, rather than a temporary directory, which can make debugging easier.
_Always remember_ to change the `DEBUG` variable to `0` when deploying. When `DEBUG` variable is `2`, the temporary folder is created and downloaded and extracted files goes to that folder, as if not in DEBUG mode, but installation is still not done. On the other hand blocking processes are checked, the app is reopened if closed, and the user is notified.
### Use Installomator with Jamf Pro Debug mode 1 is useful to test the download and verification process without having to re-download and re-install an application or package on your system. Debug mode 2 is great for checking running processe and notifications.
In Jamf Pro, create a new 'Script' and paste the contents of `Installomator.sh` into the 'Script Contents' area. Under 'Options' you can change the parameter label for argument 4 to 'Application Label.' _Always remember_ to change the `DEBUG` variable to `0` when deploying. The installer PKG we provide has `DEBUG=0`.
Remember to set `DEBUG` to `0`. ### Use Installomator with various MDM solutions
Then you can use the Installomator script in a policy and choose the application to install by setting the label for argument 4. In the wiki we have provided documentation on how Installomator is used in various MDM solution, like [Jamf Pro](https://github.com/Installomator/Installomator/wiki/MDM:-Jamf-Pro), [Mosyle](https://github.com/Installomator/Installomator/wiki/MDM:-Mosyle-(Business,-Fuse,-and-Manager)), and [Addigy](https://github.com/Installomator/Installomator/wiki/MDM:-Addigy).
## What it does ## What it does
@@ -197,7 +233,7 @@ When it runs with a known label, the script will perform the following:
- Check the version installed with the version online. Only continue if it's different - Check the version installed with the version online. Only continue if it's different
- download the latest version from the vendor - download the latest version from the vendor
- when the application is running, prompt the user to quit or cancel - when the application is running, prompt the user to quit or cancel (customizable)
- dmg or zip archives: - dmg or zip archives:
- extract the application and copy it to /Applications - extract the application and copy it to /Applications
- change the owner of the application to the current user - change the owner of the application to the current user
@@ -205,19 +241,11 @@ When it runs with a known label, the script will perform the following:
- when necessary, extract the pkg from the enclosing archive - when necessary, extract the pkg from the enclosing archive
- install the pkg with the `installer` tool - install the pkg with the `installer` tool
- clean up the downloaded files - clean up the downloaded files
- notify the user - notify the user (also customizable)
## Configuring the script ## Configuring the script
As of now there are two settings that are meant to configured when deploying the script. We have several default settings for certain behavior and notifications inside the script, but these can be customized when calling the script.
### Debug mode
The first is the `DEBUG` variable. When this is set to `1` the script will _not_ perform any changes to the current system. In other words, no application will be copied to the target directory and no `installer` command be performed.
In addition, files will be downloaded and extracted to the Installomator project folder instead of a temporary directory and _not_ deleted when the script exits. Also archives will _not_ be re-downloaded when they already exist in the project folder. The repository's `.gitignore` file is set up to ignore the archive file extensions.
Debug mode is useful to test the download and verification process without having to re-download and re-install an application or package on your system.
### Blocking Process actions ### Blocking Process actions
@@ -226,12 +254,12 @@ The `BLOCKING_PROCESS_ACTION` variable controls the behavior of the script when
There are eight options: There are eight options:
- `ignore`: continue even when blocking processes are found. - `ignore`: continue even when blocking processes are found.
- `silent_fail`: exit script without prompt or installation. - `silent_fail`: Exit script without prompt or installation.
- `prompt_user`: (default) show a user dialog for each blocking process found, user can choose "Quit and Update" or "Not Now". When "Quit and Update" is chosen, blocking process will be told to quit. Installomator will wait 30 seconds before checking again in case Save dialogs etc are being responded to. Installomator will abort if quitting after three tries does not succeed. "Not Now" will exit Installomator. - `prompt_user`: Show a user dialog for each blocking process found, user can choose "Quit and Update" or "Not Now". When "Quit and Update" is chosen, blocking process will be told to quit. Installomator will wait 30 seconds before checking again in case Save dialogs etc are being responded to. Installomator will abort if quitting after three tries does not succeed. "Not Now" will exit Installomator.
- `prompt_user_then_kill`: show a user dialog for each blocking process found, user can choose "Quit and Update" or "Not Now". When "Quit and Update" is chosen, blocking process will be terminated. Installomator will abort if terminating after two tries does not succeed. "Not Now" will exit Installomator. - `prompt_user_then_kill`: show a user dialog for each blocking process found, user can choose "Quit and Update" or "Not Now". When "Quit and Update" is chosen, blocking process will be terminated. Installomator will abort if terminating after two tries does not succeed. "Not Now" will exit Installomator.
- `prompt_user_loop`: Like prompt-user, but clicking "Not Now", will just wait an hour, and then it will ask again. - `prompt_user_loop`: Like prompt-user, but clicking "Not Now", will just wait an hour, and then it will ask again.
WARNING! It might block the MDM agent on the machine, as the script will not exit, it will pause until the hour has passed, possibly blocking for other management actions in this time. WARNING! It might block the MDM agent on the machine, as the script will not exit, it will pause until the hour has passed, possibly blocking for other management actions in this time.
- `tell_user`: User will be showed a notification about the important update, but user is only allowed to Quit and Continue, and then we ask the app to quit. This is default. - `tell_user`: (Default) User will be showed a notification about the important update, but user is only allowed to Quit and Continue, and then we ask the app to quit. This is default.
- `tell_user_then_kill`: User will be showed a notification about the important update, but user is only allowed to Quit and Continue. If the quitting fails, the blocking processes will be terminated. - `tell_user_then_kill`: User will be showed a notification about the important update, but user is only allowed to Quit and Continue. If the quitting fails, the blocking processes will be terminated.
- `kill`: kill process without prompting or giving the user a chance to save. - `kill`: kill process without prompting or giving the user a chance to save.
@@ -282,9 +310,29 @@ The `REOPEN` can be used to prevent the reopening of a closed app
- `yes`: (default) app will be reopened if it was closed - `yes`: (default) app will be reopened if it was closed
- `no`: app not reopened - `no`: app not reopened
### Adding applications/label blocks ### Configuration from Arguments
#### Required Variables You can provide a configuration variable, such as `DEBUG` or `NOTIFY` as an argument in the form `VAR=value`. For example:
```
./Installomator.sh desktoppr DEBUG=0 NOTIFY=silent
```
Providing variables this way will override any variables set in the script.
You can even provide _all_ the variables necessary for download and installation. Of course, without a label the argument parsing will fail, so I created a special label `valuesfromarguments` which only checks if the four required values are present:
```
./Installomator.sh name=desktoppr type=pkg downloadURL=https://github.com/scriptingosx/desktoppr/releases/download/v0.3/desktoppr-0.3.pkg expectedTeamID=JME5BW3F3R valuesfromarguments
```
The order of the variables and label is not relevant. But, when you provide more than one label, all but the _last_ label will be ignored.
Providing all the variables this way might be useful for certain downloads that have a customized URL for each vendor/customer (like customized TeamView or Watchman Monitoring) or are local downloads.
## Adding applications/label blocks
### Required Variables
The script requires four pieces of information to download and install an application: The script requires four pieces of information to download and install an application:
@@ -317,6 +365,12 @@ The URL from which to download the archive.
The URL can be generated by a series of commands, for example when you need to parse an xml file for the latest URL. (See `bbedit`, `desktoppr`, or `omnigraffle` for examples.) The URL can be generated by a series of commands, for example when you need to parse an xml file for the latest URL. (See `bbedit`, `desktoppr`, or `omnigraffle` for examples.)
Sometimes version differs between Intel and Apple Silicon versions. (See `brave`, `obsidian`, `omnidisksweeper`, or `notion`). Sometimes version differs between Intel and Apple Silicon versions. (See `brave`, `obsidian`, `omnidisksweeper`, or `notion`).
- `curlOptions`: (array, optional)
Options to the `curl` command, needed for `curl` to be able to download the software.
Usually used for adding extra headers that some servers need in order to serve the file.
`curlOptions=( -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15" )`
(See “mocha”-labels, for examples on labels, and `buildLabel.sh` for header-examples.)
- `appNewVersion` (optional, but recommended): - `appNewVersion` (optional, but recommended):
Version of the downloaded software. Version of the downloaded software.
If given, it will be compared to installed version, to see if download is different. If given, it will be compared to installed version, to see if download is different.
@@ -337,8 +391,7 @@ The 10-character Developer Team ID with which the application or pkg is signed a
- Installation Packages (pkg) - Installation Packages (pkg)
`spctl -a -vv -t install ~/Downloads/desktoppr-0.2.pkg` `spctl -a -vv -t install ~/Downloads/desktoppr-0.2.pkg`
### Optional Variables
#### Optional Variables
Depending on the application or pkg there are a few more variables you can or need to set. Many of these are derived from the required variables, but may need to be set manually if those derived values do not work. Depending on the application or pkg there are a few more variables you can or need to set. Many of these are derived from the required variables, but may need to be set manually if those derived values do not work.
@@ -395,42 +448,17 @@ Depending on the application or pkg there are a few more variables you can or ne
Introduced as part of `CLIInstaller`. If the installer in the DMG or ZIP is named differently than the installed app, then this variable can be used to name the installer that should be located after mounting/expanding the downloaded archive. Introduced as part of `CLIInstaller`. If the installer in the DMG or ZIP is named differently than the installed app, then this variable can be used to name the installer that should be located after mounting/expanding the downloaded archive.
See label adobecreativeclouddesktop See label adobecreativeclouddesktop
### Configuration from Arguments
You can provide a configuration variable, such as `DEBUG` or `NOTIFY` as an argument in the form `VAR=value`. For example:
```
./Installomator.sh desktoppr DEBUG=0 NOTIFY=silent
```
Providing variables this way will override any variables set in the script.
You can even provide _all_ the variables necessary for download and installation. Of course, without a label the argument parsing will fail, so I created a special label `valuesfromarguments` which only checks if the four required values are present:
```
./Installomator.sh name=desktoppr type=pkg downloadURL=https://github.com/scriptingosx/desktoppr/releases/download/v0.3/desktoppr-0.3.pkg expectedTeamID=JME5BW3F3R valuesfromarguments
```
The order of the variables and label is not relevant. But, when you provide more than one label, all but the _last_ label will be ignored.
Providing all the variables this way might be useful for certain downloads that have a customized URL for each vendor/customer (like customized TeamView or Watchman Monitoring) or are local downloads.
## Frequently Asked Questions ## Frequently Asked Questions
### What if the latest version of the app is already installed? ### What if the latest version of the app is already installed?
Short answer: Installomator will re-download and re-install the latest over the existing installation. Short answer: That depends on if labels will know what the latest version will be.
Longer answer: Longer answer:
Installomator will try to find a currently installed app to log the version. When Installomator finds an existing app (any version) and the `updateTool` variable is set, then Installomator will _not_ download and install, but run the `updateTool` instead. - Labels without this will re-download and re-install the latest over the existing installation.
- Labels with this info will only install the app if the version is different than the one installed.
However, there is no simple generic method to actually determine the latest version of an application or installer. - Labels that can use update tool will use that for the update (if the version is different)
We deploy Installomator usually for user initiated installations from Self Service, so re-installs don't really 'hurt' and may be a useful troubleshooting step.
When you want to have automated installations, you can use smart groups based on the app version to limit excessive re-installations.
### Why don't you just use `autopkg install`? ### Why don't you just use `autopkg install`?
@@ -460,7 +488,7 @@ Please don't misunderstand this as me saying that AutoPkg is a bad or poorly des
But it is not suited as a client install automation tool. But it is not suited as a client install automation tool.
### Why don't you just use brew? ### Why don't you just use brew or MacPorts?
Read the explanation for `autopkg`, pretty much the same applies for `brew`, i.e. while it is useful on a single Mac, it is a un-manageable mess when you think about deploying and managing on a fleet of computers. Read the explanation for `autopkg`, pretty much the same applies for `brew`, i.e. while it is useful on a single Mac, it is a un-manageable mess when you think about deploying and managing on a fleet of computers.

View File

@@ -43,17 +43,52 @@ if [[ $label == "version" ]]; then
exit 0 exit 0
fi fi
printlog "################## Start Installomator v. $VERSION" # MARK: Logging
printlog "################## $label" log_location="/private/var/log/Installomator.log"
# Check if we're in debug mode, if so then set logging to DEBUG, otherwise default to INFO
# if no log level is specified.
if [[ $DEBUG -ne 0 ]]; then
LOGGING=DEBUG
elif [[ -z $LOGGING ]]; then
LOGGING=INFO
datadogLoggingLevel=INFO
fi
# Associate logging levels with a numerical value so that we are able to identify what
# should be removed. For example if the LOGGING=ERROR only printlog statements with the
# level REQ and ERROR will be displayed. LOGGING=DEBUG will show all printlog statements.
# If a printlog statement has no level set it's automatically assigned INFO.
declare -A levels=(DEBUG 0 INFO 1 WARN 2 ERROR 3 REQ 4)
# If we are able to detect an MDM URL (Jamf Pro) or another identifier for a customer/instance we grab it here, this is useful if we're centrally logging multiple MDM instances.
if [[ -f /Library/Preferences/com.jamfsoftware.jamf.plist ]]; then
mdmURL=$(defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url)
elif [[ -n "$MDMProfileName" ]]; then
mdmURL=$(sudo profiles show | grep -A3 "$MDMProfileName" | sed -n -e 's/^.*organization: //p')
else
mdmURL="Unknown"
fi
# Generate a session key for this run, this is useful to idenify streams when we're centrally logging.
SESSION=$RANDOM
# Mark: START
printlog "################## Start Installomator v. $VERSION, date $VERSIONDATE" REQ
printlog "################## Version: $VERSION" INFO
printlog "################## Date: $VERSIONDATE" INFO
printlog "################## $label" INFO
# Check for DEBUG mode # Check for DEBUG mode
if [[ $DEBUG -gt 0 ]]; then if [[ $DEBUG -gt 0 ]]; then
printlog "DEBUG mode $DEBUG enabled." printlog "DEBUG mode $DEBUG enabled." DEBUG
fi fi
# How we get version number from app # How we get version number from app
# (alternative is "CFBundleVersion", that can be used in labels) if [[ -z $versionKey ]]; then
versionKey="CFBundleShortVersionString" versionKey="CFBundleShortVersionString"
fi
# get current user # get current user
currentUser=$(scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ { print $3 }') currentUser=$(scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ { print $3 }')

View File

@@ -1,7 +0,0 @@
mochakeyboard)
name="Mocha Keyboard"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/mochakeyboard.dmg.zip"
appNewVersion=""
expectedTeamID="RR9F5EPNVW"
;;

View File

@@ -1,7 +0,0 @@
mochatelnet)
name="Telnet"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/telnet.dmg.zip"
appNewVersion=""
expectedTeamID="RR9F5EPNVW"
;;

View File

@@ -1,7 +0,0 @@
mochatn3270)
name="TN3270"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/tn3270.dmg.zip"
appNewVersion=""
expectedTeamID="RR9F5EPNVW"
;;

View File

@@ -1,7 +0,0 @@
mochatn3812)
name="TN3812"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/tn3812.dmg.zip"
appNewVersion=""
expectedTeamID="Frydendal"
;;

View File

@@ -1,7 +0,0 @@
mochatn5250)
name="TN5250"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/tn5250.dmg.zip"
appNewVersion=""
expectedTeamID="RR9F5EPNVW"
;;

View File

@@ -1,26 +1,29 @@
# MARK: Functions # MARK: Functions
cleanupAndExit() { # $1 = exit code, $2 message cleanupAndExit() { # $1 = exit code, $2 message, $3 level
if [[ -n $2 && $1 -ne 0 ]]; then
printlog "ERROR: $2"
fi
if [ "$DEBUG" -ne 1 ]; then
# remove the temporary working directory when done
printlog "Deleting $tmpDir"
rm -Rf "$tmpDir"
fi
if [ -n "$dmgmount" ]; then if [ -n "$dmgmount" ]; then
# unmount disk image # unmount disk image
printlog "Unmounting $dmgmount" printlog "Unmounting $dmgmount" DEBUG
hdiutil detach "$dmgmount" unmountingOut=$(hdiutil detach "$dmgmount" 2>&1)
printlog "Debugging enabled, Unmounting output was:\n$unmountingOut" DEBUG
fi fi
if [ "$DEBUG" -ne 1 ]; then
# remove the temporary working directory when done (only if DEBUG is not used)
printlog "Deleting $tmpDir" DEBUG
deleteTmpOut=$(rm -Rfv "$tmpDir")
printlog "Debugging enabled, Deleting tmpDir output was:\n$deleteTmpOut" DEBUG
fi
# If we closed any processes, reopen the app again # If we closed any processes, reopen the app again
reopenClosedProcess reopenClosedProcess
printlog "################## End Installomator, exit code $1 \n\n" if [[ -n $2 && $1 -ne 0 ]]; then
printlog "ERROR: $2" $3
fi
printlog "################## End Installomator, exit code $1 \n\n" REQ
# if label is wrong and we wanted name of the label, then return ################## # if label is wrong and we wanted name of the label, then return ##################
if [[ $RETURN_LABEL_NAME -eq 1 ]]; then if [[ $RETURN_LABEL_NAME -eq 1 ]]; then
1=0 1=0 # If only label name should be returned we exit without any errors
echo "#" echo "#"
fi fi
exit "$1" exit "$1"
@@ -64,19 +67,77 @@ displaynotification() { # $1: message $2: title
fi fi
} }
# MARK: Logging
log_location="/private/var/log/Installomator.log"
printlog(){ printlog(){
[ -z "$2" ] && 2=INFO
log_message=$1
log_priority=$2
timestamp=$(date +%F\ %T) timestamp=$(date +%F\ %T)
if [[ "$(whoami)" == "root" ]]; then # Check to make sure that the log isn't the same as the last, if it is then don't log and increment a timer.
echo "$timestamp" "$label" "$1" | tee -a $log_location if [[ ${log_message} == ${previous_log_message} ]]; then
else let logrepeat=$logrepeat+1
echo "$timestamp" "$label" "$1" return
fi fi
previous_log_message=$log_message
# Once we finally stop getting duplicate logs output the number of times we got a duplicate.
if [[ $logrepeat -gt 1 ]];then
echo "$timestamp" : "${log_priority} : $label : Last Log repeated ${logrepeat} times" | tee -a $log_location
if [[ ! -z $datadogAPI ]]; then
curl -s -X POST https://http-intake.logs.datadoghq.com/v1/input -H "Content-Type: text/plain" -H "DD-API-KEY: $datadogAPI" -d "${log_priority} : $mdmURL : $APPLICATION : $VERSION : $SESSION : Last Log repeated ${logrepeat} times" > /dev/null
fi
logrepeat=0
fi
# If the datadogAPI key value is set and our logging level is greater than or equal to our set level
# then post to Datadog's HTTPs endpoint.
if [[ -n $datadogAPI && ${levels[$log_priority]} -ge ${levels[$datadogLoggingLevel]} ]]; then
while IFS= read -r logmessage; do
curl -s -X POST https://http-intake.logs.datadoghq.com/v1/input -H "Content-Type: text/plain" -H "DD-API-KEY: $datadogAPI" -d "${log_priority} : $mdmURL : Installomator-${label} : ${VERSIONDATE//-/} : $SESSION : ${logmessage}" > /dev/null
done <<< "$log_message"
fi
# Extra spaces
space_char=""
if [[ ${#log_priority} -eq 3 ]]; then
space_char=" "
elif [[ ${#log_priority} -eq 4 ]]; then
space_char=" "
fi
# If our logging level is greaterthan or equal to our set level then output locally.
if [[ ${levels[$log_priority]} -ge ${levels[$LOGGING]} ]]; then
while IFS= read -r logmessage; do
if [[ "$(whoami)" == "root" ]]; then
echo "$timestamp" : "${log_priority}${space_char} : $label : ${logmessage}" | tee -a $log_location
else
echo "$timestamp" : "${log_priority}${space_char} : $label : ${logmessage}"
fi
done <<< "$log_message"
fi
}
# Used to remove dupplicate lines in large log output,
# for example from msupdate command after it finishes running.
deduplicatelogs() {
loginput=${1:-"Log"}
logoutput=""
# Read each line of the incoming log individually, match it with the previous.
# If it matches increment logrepeate then skip to the next line.
while read log; do
if [[ $log == $previous_log ]];then
let logrepeat=$logrepeat+1
continue
fi
previous_log="$log"
if [[ $logrepeat -gt 1 ]];then
logoutput+="Last Log repeated ${logrepeat} times\n"
logrepeat=0
fi
logoutput+="$log\n"
done <<< "$loginput"
} }
# will get the latest release download from a github repo # will get the latest release download from a github repo
@@ -100,8 +161,7 @@ downloadURLFromGit() { # $1 git user name, $2 git repo name
| awk -F '"' "/browser_download_url/ && /$filetype\"/ { print \$4; exit }") | awk -F '"' "/browser_download_url/ && /$filetype\"/ { print \$4; exit }")
fi fi
if [ -z "$downloadURL" ]; then if [ -z "$downloadURL" ]; then
cleanupAndExit 9 "could not retrieve download URL for $gitusername/$gitreponame" cleanupAndExit 9 "could not retrieve download URL for $gitusername/$gitreponame" ERROR
#exit 9
else else
echo "$downloadURL" echo "$downloadURL"
return 0 return 0
@@ -169,9 +229,9 @@ getAppVersion() {
applist=$(mdfind "kind:application $appName" -0 ) applist=$(mdfind "kind:application $appName" -0 )
fi fi
if [[ -z applist ]]; then if [[ -z applist ]]; then
printlog "No previous app found" printlog "No previous app found" DEBUG
else else
printlog "App(s) found: ${applist}" printlog "App(s) found: ${applist}" DEBUG
fi fi
appPathArray=( ${(0)applist} ) appPathArray=( ${(0)applist} )
@@ -182,7 +242,7 @@ getAppVersion() {
installedAppPath=$filteredAppPaths[1] installedAppPath=$filteredAppPaths[1]
#appversion=$(mdls -name kMDItemVersion -raw $installedAppPath ) #appversion=$(mdls -name kMDItemVersion -raw $installedAppPath )
appversion=$(defaults read $installedAppPath/Contents/Info.plist $versionKey) #Not dependant on Spotlight indexing appversion=$(defaults read $installedAppPath/Contents/Info.plist $versionKey) #Not dependant on Spotlight indexing
printlog "found app at $installedAppPath, version $appversion" printlog "found app at $installedAppPath, version $appversion, on versionKey $versionKey"
updateDetected="YES" updateDetected="YES"
# Is current app from App Store # Is current app from App Store
if [[ -d "$installedAppPath"/Contents/_MASReceipt ]];then if [[ -d "$installedAppPath"/Contents/_MASReceipt ]];then
@@ -191,7 +251,7 @@ getAppVersion() {
printlog "Replacing App Store apps, no matter the version" printlog "Replacing App Store apps, no matter the version"
appversion=0 appversion=0
else else
cleanupAndExit 1 "App previously installed from App Store, and we respect that" cleanupAndExit 1 "App previously installed from App Store, and we respect that" ERROR
fi fi
fi fi
else else
@@ -205,7 +265,7 @@ getAppVersion() {
checkRunningProcesses() { checkRunningProcesses() {
# don't check in DEBUG mode 1 # don't check in DEBUG mode 1
if [[ $DEBUG -eq 1 ]]; then if [[ $DEBUG -eq 1 ]]; then
printlog "DEBUG mode 1, not checking for blocking processes" printlog "DEBUG mode 1, not checking for blocking processes" DEBUG
return return
fi fi
@@ -238,7 +298,7 @@ checkRunningProcesses() {
prompt_user|prompt_user_then_kill) prompt_user|prompt_user_then_kill)
button=$(displaydialog "Quit “$x” to continue updating? (Leave this dialogue if you want to activate this update later)." "The application “$x” needs to be updated.") button=$(displaydialog "Quit “$x” to continue updating? (Leave this dialogue if you want to activate this update later)." "The application “$x” needs to be updated.")
if [[ $button = "Not Now" ]]; then if [[ $button = "Not Now" ]]; then
cleanupAndExit 10 "user aborted update" cleanupAndExit 10 "user aborted update" ERROR
else else
if [[ $i > 2 && $BLOCKING_PROCESS_ACTION = "prompt_user_then_kill" ]]; then if [[ $i > 2 && $BLOCKING_PROCESS_ACTION = "prompt_user_then_kill" ]]; then
printlog "Changing BLOCKING_PROCESS_ACTION to kill" printlog "Changing BLOCKING_PROCESS_ACTION to kill"
@@ -283,7 +343,7 @@ checkRunningProcesses() {
fi fi
;; ;;
silent_fail) silent_fail)
cleanupAndExit 12 "blocking process '$x' found, aborting" cleanupAndExit 12 "blocking process '$x' found, aborting" ERROR
;; ;;
esac esac
@@ -294,10 +354,10 @@ checkRunningProcesses() {
done done
if [[ $countedProcesses -ne 0 ]]; then if [[ $countedProcesses -ne 0 ]]; then
cleanupAndExit 11 "could not quit all processes, aborting..." cleanupAndExit 11 "could not quit all processes, aborting..." ERROR
fi fi
printlog "no more blocking processes, continue with update" printlog "no more blocking processes, continue with update" REQ
} }
reopenClosedProcess() { reopenClosedProcess() {
@@ -312,7 +372,7 @@ reopenClosedProcess() {
# don't reopen in DEBUG mode 1 # don't reopen in DEBUG mode 1
if [[ $DEBUG -eq 1 ]]; then if [[ $DEBUG -eq 1 ]]; then
printlog "DEBUG mode 1, not reopening anything" printlog "DEBUG mode 1, not reopening anything" DEBUG
return return
fi fi
@@ -325,7 +385,7 @@ reopenClosedProcess() {
processuser=$(ps aux | grep -i "${appName}" | grep -vi "grep" | awk '{print $1}') processuser=$(ps aux | grep -i "${appName}" | grep -vi "grep" | awk '{print $1}')
printlog "Reopened ${appName} as $processuser" printlog "Reopened ${appName} as $processuser"
else else
printlog "App not closed, so no reopen." printlog "App not closed, so no reopen." DEBUG
fi fi
} }
@@ -335,42 +395,49 @@ installAppWithPath() { # $1: path to app to install in $targetDir
# check if app exists # check if app exists
if [ ! -e "$appPath" ]; then if [ ! -e "$appPath" ]; then
cleanupAndExit 8 "could not find: $appPath" cleanupAndExit 8 "could not find: $appPath" DEBUG
fi fi
# verify with spctl # verify with spctl
printlog "Verifying: $appPath" printlog "Verifying: $appPath" INFO
if ! teamID=$(spctl -a -vv "$appPath" 2>&1 | awk '/origin=/ {print $NF }' | tr -d '()' ); then printlog "App size: $(du -sh "$appPath")" DEBUG
cleanupAndExit 4 "Error verifying $appPath" appVerify=$(spctl -a -vv "$appPath" 2>&1 )
fi appVerifyStatus=$(echo $?)
teamID=$(echo $appVerify | awk '/origin=/ {print $NF }' | tr -d '()' )
deduplicatelogs "$appVerify"
printlog "Team ID matching: $teamID (expected: $expectedTeamID )" if [[ $appVerifyStatus -ne 0 ]] ; then
#if ! teamID=$(spctl -a -vv "$appPath" 2>&1 | awk '/origin=/ {print $NF }' | tr -d '()' ); then
cleanupAndExit 4 "Error verifying $appPath error:\n$logoutput" ERROR
fi
printlog "Debugging enabled, App Verification output was:\n$logoutput" DEBUG
printlog "Team ID matching: $teamID (expected: $expectedTeamID )" INFO
if [ "$expectedTeamID" != "$teamID" ]; then if [ "$expectedTeamID" != "$teamID" ]; then
cleanupAndExit 5 "Team IDs do not match" cleanupAndExit 5 "Team IDs do not match" ERROR
fi fi
# app versioncheck # app versioncheck
appNewVersion=$(defaults read $appPath/Contents/Info.plist $versionKey) appNewVersion=$(defaults read $appPath/Contents/Info.plist $versionKey)
if [[ -n $appNewVersion && $appversion == $appNewVersion ]]; then if [[ -n $appNewVersion && $appversion == $appNewVersion ]]; then
printlog "Downloaded version of $name is $appNewVersion, same as installed." printlog "Downloaded version of $name is $appNewVersion on versionKey $versionKey, same as installed."
if [[ $INSTALL != "force" ]]; then if [[ $INSTALL != "force" ]]; then
message="$name, version $appNewVersion, is the latest version." message="$name, version $appNewVersion, is the latest version."
if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then
printlog "notifying" printlog "notifying"
displaynotification "$message" "No update for $name!" displaynotification "$message" "No update for $name!"
fi fi
cleanupAndExit 0 "No new version to install" cleanupAndExit 0 "No new version to install" INFO
else else
printlog "Using force to install anyway." printlog "Using force to install anyway."
fi fi
else else
printlog "Downloaded version of $name is $appNewVersion (replacing version $appversion)." printlog "Downloaded version of $name is $appNewVersion on versionKey $versionKey (replacing version $appversion)."
fi fi
# macOS versioncheck # macOS versioncheck
minimumOSversion=$(defaults read $appPath/Contents/Info.plist LSMinimumSystemVersion) minimumOSversion=$(defaults read $appPath/Contents/Info.plist LSMinimumSystemVersion 2>/dev/null )
if [[ $minimumOSversion =~ '[0-9.]*' ]]; then if [[ -n $minimumOSversion && $minimumOSversion =~ '[0-9.]*' ]]; then
printlog "App has LSMinimumSystemVersion: $minimumOSversion" printlog "App has LSMinimumSystemVersion: $minimumOSversion"
if ! is-at-least $minimumOSversion $installedOSversion; then if ! is-at-least $minimumOSversion $installedOSversion; then
printlog "App requires higher System Version than installed: $installedOSversion" printlog "App requires higher System Version than installed: $installedOSversion"
@@ -379,19 +446,19 @@ installAppWithPath() { # $1: path to app to install in $targetDir
printlog "notifying" printlog "notifying"
displaynotification "$message" "Error updating $name!" displaynotification "$message" "Error updating $name!"
fi fi
cleanupAndExit 6 "Installed macOS is too old for this app." cleanupAndExit 6 "Installed macOS is too old for this app." INFO
fi fi
fi fi
# skip install for DEBUG 1 # skip install for DEBUG 1
if [ "$DEBUG" -eq 1 ]; then if [ "$DEBUG" -eq 1 ]; then
printlog "DEBUG mode 1 enabled, skipping remove, copy and chown steps" printlog "DEBUG mode 1 enabled, skipping remove, copy and chown steps" DEBUG
return 0 return 0
fi fi
# skip install for DEBUG 2 # skip install for DEBUG 2
if [ "$DEBUG" -eq 2 ]; then if [ "$DEBUG" -eq 2 ]; then
printlog "DEBUG mode 2 enabled, exiting" printlog "DEBUG mode 2 enabled, not installing anything, exiting" DEBUG
cleanupAndExit 0 cleanupAndExit 0
fi fi
@@ -400,14 +467,19 @@ installAppWithPath() { # $1: path to app to install in $targetDir
# remove existing application # remove existing application
if [ -e "$targetDir/$appName" ]; then if [ -e "$targetDir/$appName" ]; then
printlog "Removing existing $targetDir/$appName" printlog "Removing existing $targetDir/$appName" DEBUG
rm -Rf "$targetDir/$appName" deleteAppOut=$(rm -Rfv "$targetDir/$appName" 2>&1)
tempName="$targetDir/$appName"
tempNameLength=$((${#tempName} + 10))
deleteAppOut=$(echo $deleteAppOut | cut -c 1-$tempNameLength)
deduplicatelogs "$deleteAppOut"
printlog "Debugging enabled, App removing output was:\n$logoutput" DEBUG
fi fi
# copy app to /Applications # copy app to /Applications
printlog "Copy $appPath to $targetDir" printlog "Copy $appPath to $targetDir"
if ! ditto "$appPath" "$targetDir/$appName"; then if ! ditto "$appPath" "$targetDir/$appName"; then
cleanupAndExit 7 "Error while copying" cleanupAndExit 7 "Error while copying" ERROR
fi fi
# set ownership to current user # set ownership to current user
@@ -415,24 +487,24 @@ installAppWithPath() { # $1: path to app to install in $targetDir
printlog "Changing owner to $currentUser" printlog "Changing owner to $currentUser"
chown -R "$currentUser" "$targetDir/$appName" chown -R "$currentUser" "$targetDir/$appName"
else else
printlog "No user logged in or SYSTEMOWNER=1, setting owner to root:wheel" printlog "No user logged in or SYSTEMOWNER=1, setting owner to root:wheel"
chown -R root:wheel "$targetDir/$appName" chown -R root:wheel "$targetDir/$appName"
fi fi
elif [[ ! -z $CLIInstaller ]]; then elif [[ ! -z $CLIInstaller ]]; then
mountname=$(dirname $appPath) mountname=$(dirname $appPath)
printlog "CLIInstaller exists, running installer command $mountname/$CLIInstaller $CLIArguments" #INFO printlog "CLIInstaller exists, running installer command $mountname/$CLIInstaller $CLIArguments" INFO
CLIoutput=$("$mountname/$CLIInstaller" "${CLIArguments[@]}" 2>&1) CLIoutput=$("$mountname/$CLIInstaller" "${CLIArguments[@]}" 2>&1)
CLIstatus=$(echo $?) CLIstatus=$(echo $?)
logoutput="$CLIoutput" # dedupliatelogs "$CLIoutput" deduplicatelogs "$CLIoutput"
if [ $CLIstatus -ne 0 ] ; then if [ $CLIstatus -ne 0 ] ; then
cleanupAndExit 3 "Error installing $mountname/$CLIInstaller $CLIArguments error:\n$logoutput" #ERROR cleanupAndExit 3 "Error installing $mountname/$CLIInstaller $CLIArguments error:\n$logoutput" ERROR
else else
printlog "Succesfully ran $mountname/$CLIInstaller $CLIArguments" printlog "Succesfully ran $mountname/$CLIInstaller $CLIArguments" INFO
fi fi
printlog "Debugging enabled, update tool output was:\n$logoutput" #DEBUG printlog "Debugging enabled, update tool output was:\n$logoutput" DEBUG
fi fi
} }
@@ -441,16 +513,21 @@ mountDMG() {
# mount the dmg # mount the dmg
printlog "Mounting $tmpDir/$archiveName" printlog "Mounting $tmpDir/$archiveName"
# always pipe 'Y\n' in case the dmg requires an agreement # always pipe 'Y\n' in case the dmg requires an agreement
if ! dmgmount=$(echo 'Y'$'\n' | hdiutil attach "$tmpDir/$archiveName" -nobrowse -readonly | tail -n 1 | cut -c 54- ); then dmgmountOut=$(echo 'Y'$'\n' | hdiutil attach "$tmpDir/$archiveName" -nobrowse -readonly )
cleanupAndExit 3 "Error mounting $tmpDir/$archiveName" dmgmountStatus=$(echo $?)
dmgmount=$(echo $dmgmountOut | tail -n 1 | cut -c 54- )
deduplicatelogs "$dmgmountOut"
if [[ $dmgmountStatus -ne 0 ]] ; then
#if ! dmgmount=$(echo 'Y'$'\n' | hdiutil attach "$tmpDir/$archiveName" -nobrowse -readonly | tail -n 1 | cut -c 54- ); then
cleanupAndExit 3 "Error mounting $tmpDir/$archiveName error:\n$logoutput" ERROR
fi fi
if [[ ! -e $dmgmount ]]; then if [[ ! -e $dmgmount ]]; then
printlog "Error mounting $tmpDir/$archiveName" cleanupAndExit 3 "Error accessing mountpoint for $tmpDir/$archiveName error:\n$logoutput" ERROR
cleanupAndExit 3
fi fi
printlog "Debugging enabled, dmgmount output was:\n$logoutput" DEBUG
printlog "Mounted: $dmgmount"
printlog "Mounted: $dmgmount" INFO
} }
installFromDMG() { installFromDMG() {
@@ -461,25 +538,34 @@ installFromDMG() {
installFromPKG() { installFromPKG() {
# verify with spctl # verify with spctl
printlog "Verifying: $archiveName" printlog "Verifying: $archiveName"
printlog "File list: $(ls -lh "$archiveName")" DEBUG
if ! spctlout=$(spctl -a -vv -t install "$archiveName" 2>&1 ); then printlog "File type: $(file "$archiveName")" DEBUG
printlog "Error verifying $archiveName" spctlOut=$(spctl -a -vv -t install "$archiveName" 2>&1 )
cleanupAndExit 4 spctlStatus=$(echo $?)
printlog "spctlOut is $spctlOut" DEBUG
teamID=$(echo $spctlOut | awk -F '(' '/origin=/ {print $2 }' | tr -d '()' )
# Apple signed software has no teamID, grab entire origin instead
if [[ -z $teamID ]]; then
teamID=$(echo $spctlOut | awk -F '=' '/origin=/ {print $NF }')
fi fi
teamID=$(echo $spctlout | awk -F '(' '/origin=/ {print $2 }' | tr -d '()' ) deduplicatelogs "$spctlOut"
if [[ $spctlStatus -ne 0 ]] ; then
#if ! spctlout=$(spctl -a -vv -t install "$archiveName" 2>&1 ); then
cleanupAndExit 4 "Error verifying $archiveName error:\n$logoutput" ERROR
fi
# Apple signed software has no teamID, grab entire origin instead # Apple signed software has no teamID, grab entire origin instead
if [[ -z $teamID ]]; then if [[ -z $teamID ]]; then
teamID=$(echo $spctlout | awk -F '=' '/origin=/ {print $NF }') teamID=$(echo $spctlout | awk -F '=' '/origin=/ {print $NF }')
fi fi
printlog "Team ID: $teamID (expected: $expectedTeamID )" printlog "Team ID: $teamID (expected: $expectedTeamID )"
if [ "$expectedTeamID" != "$teamID" ]; then if [ "$expectedTeamID" != "$teamID" ]; then
printlog "Team IDs do not match!" cleanupAndExit 5 "Team IDs do not match!" ERROR
cleanupAndExit 5
fi fi
# Check version of pkg to be installed if packageID is set # Check version of pkg to be installed if packageID is set
@@ -499,7 +585,7 @@ installFromPKG() {
printlog "notifying" printlog "notifying"
displaynotification "$message" "No update for $name!" displaynotification "$message" "No update for $name!"
fi fi
cleanupAndExit 0 "No new version to install" cleanupAndExit 0 "No new version to install" INFO
else else
printlog "Using force to install anyway." printlog "Using force to install anyway."
fi fi
@@ -508,22 +594,38 @@ installFromPKG() {
# skip install for DEBUG 1 # skip install for DEBUG 1
if [ "$DEBUG" -eq 1 ]; then if [ "$DEBUG" -eq 1 ]; then
printlog "DEBUG enabled, skipping installation" printlog "DEBUG enabled, skipping installation" DEBUG
return 0 return 0
fi fi
# skip install for DEBUG 2 # skip install for DEBUG 2
if [ "$DEBUG" -eq 2 ]; then if [ "$DEBUG" -eq 2 ]; then
printlog "DEBUG mode 2 enabled, exiting" cleanupAndExit 0 "DEBUG mode 2 enabled, exiting" DEBUG
cleanupAndExit 0
fi fi
# install pkg # install pkg
printlog "Installing $archiveName to $targetDir" printlog "Installing $archiveName to $targetDir"
if ! installer -pkg "$archiveName" -tgt "$targetDir" ; then pkgInstall=$(installer -verbose -dumplog -pkg "$archiveName" -tgt "$targetDir" 2>&1)
printlog "error installing $archiveName" pkgInstallStatus=$(echo $?)
cleanupAndExit 9 sleep 1
pkgEndTime=$(date "+$LogDateFormat")
pkgInstall+=$(echo "\nOutput of /var/log/install.log below this line.\n")
pkgInstall+=$(echo "----------------------------------------------------------\n")
pkgInstall+=$(awk -v "b=$starttime" -v "e=$pkgEndTime" -F ',' '$1 >= b && $1 <= e' /var/log/install.log)
deduplicatelogs "$pkgInstall"
if [[ $pkgInstallStatus -ne 0 ]] && [[ $logoutput == *"requires Rosetta 2"* ]] && [[ $rosetta2 == no ]]; then
printlog "Package requires Rosetta 2, Installing Rosetta 2 and Installing Package" INFO
/usr/sbin/softwareupdate --install-rosetta --agree-to-license
rosetta2=yes
installFromPKG
fi fi
if [[ $pkginstallstatus -ne 0 ]] ; then
#if ! installer -pkg "$archiveName" -tgt "$targetDir" ; then
cleanupAndExit 9 "Error installing $archiveName error:\n$logoutput" ERROR
fi
printlog "Debugging enabled, installer output was:\n$logoutput" DEBUG
} }
installFromZIP() { installFromZIP() {
@@ -556,10 +658,10 @@ installPkgInDmg() {
# locate pkg in dmg # locate pkg in dmg
if [[ -z $pkgName ]]; then if [[ -z $pkgName ]]; then
# find first file ending with 'pkg' # find first file ending with 'pkg'
findfiles=$(find "$dmgmount" -iname "*.pkg" -maxdepth 1 ) findfiles=$(find "$dmgmount" -iname "*.pkg" -type f -maxdepth 1 )
filearray=( ${(f)findfiles} ) filearray=( ${(f)findfiles} )
if [[ ${#filearray} -eq 0 ]]; then if [[ ${#filearray} -eq 0 ]]; then
cleanupAndExit 20 "couldn't find pkg in dmg $archiveName" cleanupAndExit 20 "couldn't find pkg in dmg $archiveName" ERROR
fi fi
archiveName="${filearray[1]}" archiveName="${filearray[1]}"
printlog "found pkg: $archiveName" printlog "found pkg: $archiveName"
@@ -571,7 +673,7 @@ installPkgInDmg() {
findfiles=$(find "$tmpDir" -iname "$pkgName") findfiles=$(find "$tmpDir" -iname "$pkgName")
filearray=( ${(f)findfiles} ) filearray=( ${(f)findfiles} )
if [[ ${#filearray} -eq 0 ]]; then if [[ ${#filearray} -eq 0 ]]; then
cleanupAndExit 20 "couldn't find pkg “$pkgName” in zip $archiveName" cleanupAndExit 20 "couldn't find pkg “$pkgName” in zip $archiveName" ERROR
fi fi
# it is now safe to overwrite archiveName for installFromPKG # it is now safe to overwrite archiveName for installFromPKG
archiveName="${filearray[1]}" archiveName="${filearray[1]}"
@@ -591,23 +693,23 @@ installPkgInZip() {
# locate pkg in zip # locate pkg in zip
if [[ -z $pkgName ]]; then if [[ -z $pkgName ]]; then
# find first file ending with 'pkg' # find first file ending with 'pkg'
findfiles=$(find "$tmpDir" -iname "*.pkg" -maxdepth 2 ) findfiles=$(find "$tmpDir" -iname "*.pkg" -type f -maxdepth 2 )
filearray=( ${(f)findfiles} ) filearray=( ${(f)findfiles} )
if [[ ${#filearray} -eq 0 ]]; then if [[ ${#filearray} -eq 0 ]]; then
cleanupAndExit 20 "couldn't find pkg in zip $archiveName" cleanupAndExit 20 "couldn't find pkg in zip $archiveName" ERROR
fi fi
# it is now safe to overwrite archiveName for installFromPKG # it is now safe to overwrite archiveName for installFromPKG
archiveName="${filearray[1]}" archiveName="${filearray[1]}"
printlog "found pkg: $archiveName" printlog "found pkg: $archiveName"
else else
if ls "$tmpDir/$pkgName" ; then if [[ -s "$tmpDir/$pkgName" ]]; then
archiveName="$tmpDir/$pkgName" archiveName="$tmpDir/$pkgName"
else else
# try searching for pkg # try searching for pkg
findfiles=$(find "$tmpDir" -iname "$pkgName") findfiles=$(find "$tmpDir" -iname "$pkgName")
filearray=( ${(f)findfiles} ) filearray=( ${(f)findfiles} )
if [[ ${#filearray} -eq 0 ]]; then if [[ ${#filearray} -eq 0 ]]; then
cleanupAndExit 20 "couldn't find pkg “$pkgName” in zip $archiveName" cleanupAndExit 20 "couldn't find pkg “$pkgName” in zip $archiveName" ERROR
fi fi
# it is now safe to overwrite archiveName for installFromPKG # it is now safe to overwrite archiveName for installFromPKG
archiveName="${filearray[1]}" archiveName="${filearray[1]}"
@@ -630,7 +732,7 @@ installAppInDmgInZip() {
findfiles=$(find "$tmpDir" -iname "*.dmg" -maxdepth 2 ) findfiles=$(find "$tmpDir" -iname "*.dmg" -maxdepth 2 )
filearray=( ${(f)findfiles} ) filearray=( ${(f)findfiles} )
if [[ ${#filearray} -eq 0 ]]; then if [[ ${#filearray} -eq 0 ]]; then
cleanupAndExit 20 "couldn't find dmg in zip $archiveName" cleanupAndExit 20 "couldn't find dmg in zip $archiveName" ERROR
fi fi
archiveName="$(basename ${filearray[1]})" archiveName="$(basename ${filearray[1]})"
# it is now safe to overwrite archiveName for installFromDMG # it is now safe to overwrite archiveName for installFromDMG
@@ -649,12 +751,29 @@ runUpdateTool() {
if [[ -x $updateTool ]]; then if [[ -x $updateTool ]]; then
printlog "running $updateTool $updateToolArguments" printlog "running $updateTool $updateToolArguments"
if [[ -n $updateToolRunAsCurrentUser ]]; then if [[ -n $updateToolRunAsCurrentUser ]]; then
runAsUser $updateTool ${updateToolArguments} updateOutput=$(runAsUser $updateTool ${updateToolArguments} 2>&1)
updateStatus=$(echo $?)
else else
$updateTool ${updateToolArguments} updateOutput=$($updateTool ${updateToolArguments} 2>&1)
updateStatus=$(echo $?)
fi fi
if [[ $? -ne 0 ]]; then sleep 1
cleanupAndExit 15 "Error running $updateTool" updateEndTime=$(date "+$updateToolLogDateFormat")
deduplicatelogs $updateOutput
if [[ -n $updateToolLog ]]; then
updateOutput+=$(echo "Output of Installer log of $updateToolLog below this line.\n")
updateOutput+=$(echo "----------------------------------------------------------\n")
updateOutput+=$(awk -v "b=$updatestarttime" -v "e=$updateEndTime" -F ',' '$1 >= b && $1 <= e' $updateToolLog)
fi
if [[ $updateStatus -ne 0 ]]; then
printlog "Error running $updateTool, Procceding with normal installation. Exit Status: $updateStatus Error:\n$logoutput" WARN
return 1
if [[ $type == updateronly ]]; then
cleanupAndExit 77 "No Download URL Set, this is an update only application and the updater failed" WARN
fi
elif [[ $updateStatus -eq 0 ]]; then
printlog "Debugging enabled, update tool output was:\n$logoutput" DEBUG
fi fi
else else
printlog "couldn't find $updateTool, continuing normally" printlog "couldn't find $updateTool, continuing normally"
@@ -686,4 +805,30 @@ finishing() {
fi fi
} }
# Detect if there is an app actively making a display sleep assertion, e.g.
# KeyNote, PowerPoint, Zoom, or Webex.
# See: https://developer.apple.com/documentation/iokit/iopmlib_h/iopmassertiontypes
hasDisplaySleepAssertion() {
# Get the names of all apps with active display sleep assertions
local apps="$(/usr/bin/pmset -g assertions | /usr/bin/awk '/NoDisplaySleepAssertion | PreventUserIdleDisplaySleep/ && match($0,/\(.+\)/) && ! /coreaudiod/ {gsub(/^.*\(/,"",$0); gsub(/\).*$/,"",$0); print};')"
if [[ ! "${apps}" ]]; then
# No display sleep assertions detected
return 1
fi
# Create an array of apps that need to be ignored
local ignore_array=("${(@s/,/)IGNORE_DND_APPS}")
for app in ${(f)apps}; do
if (( ! ${ignore_array[(Ie)${app}]} )); then
# Relevant app with display sleep assertion detected
printlog "Display sleep assertion detected by ${app}."
return 0
fi
done
# No relevant display sleep assertion detected
return 1
}

View File

@@ -126,6 +126,18 @@ REOPEN="yes"
# instead of just the label name. # instead of just the label name.
# Interrupt Do Not Disturb (DND) full screen apps
INTERRUPT_DND="yes"
# options:
# - yes Script will run without checking for DND full screen apps.
# - no Script will exit when an active DND full screen app is detected.
# Comma separated list of app names to ignore when evaluating DND
IGNORE_DND_APPS=""
# example that will ignore browsers when evaluating DND:
# IGNORE_DND_APPS="firefox,Google Chrome,Safari,Microsoft Edge,Opera,Amphetamine,caffeinate"
# NOTE: How labels work # NOTE: How labels work
# Each workflow label needs to be listed in the case statement below. # Each workflow label needs to be listed in the case statement below.
@@ -156,6 +168,12 @@ REOPEN="yes"
# URL to download the dmg. # URL to download the dmg.
# Can be generated with a series of commands (see BBEdit for an example). # Can be generated with a series of commands (see BBEdit for an example).
# #
# - curlOptions: (array, optional)
# Options to the curl command, needed for curl to be able to download the software.
# Usually used for adding extra headers that some servers need in order to serve the file.
# curlOptions=( -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15" )
# (See “mocha”-labels, for examples on labels, and buildLabel.sh for header-examples.)
#
# - appNewVersion: (optional) # - appNewVersion: (optional)
# Version of the downloaded software. # Version of the downloaded software.
# If given, it will be compared to the installed version, to see if the download is different. # If given, it will be compared to the installed version, to see if the download is different.
@@ -246,3 +264,40 @@ REOPEN="yes"
# installer that should be located after mounting/expanding the downloaded archive. # installer that should be located after mounting/expanding the downloaded archive.
# See label adobecreativeclouddesktop # See label adobecreativeclouddesktop
# #
### Logging
# Logging behavior
LOGGING="INFO"
# options:
# - DEBUG Everything is logged
# - INFO (default) normal logging level
# - WARN only warning
# - ERROR only errors
# - REQ ????
# MDM profile name
MDMProfileName=""
# options:
# - MDM Profile Addigy has this name on the profile
# - Mosyle Corporation MDM Mosyle uses this name on the profile
# From the LOGO variable we can know if Addigy og Mosyle is used, so if that variable
# is either of these, and this variable is empty, then we will auto detect this.
# Datadog logging used
datadogAPI=""
# Simply add your own API key for this in order to have logs sent to Datadog
# See more here: https://www.datadoghq.com/product/log-management/
# Log Date format used when parsing logs for debugging, this is the default used by
# install.log, override this in the case statements if you need something custom per
# application (See adobeillustrator). Using stadard GNU Date formatting.
LogDateFormat="%Y-%m-%d %H:%M:%S"
# Get the start time for parsing install.log if we fail.
starttime=$(date "+$LogDateFormat")
# Check if we have rosetta installed
if [[ $(/usr/bin/arch) == "arm64" ]]; then
if ! arch -x86_64 /usr/bin/true >/dev/null 2>&1; then # pgrep oahd >/dev/null 2>&1
rosetta2=no
fi
fi

View File

@@ -0,0 +1,8 @@
diskspace)
name="diskspace"
type="pkg"
packageID="com.scriptingosx.diskspace"
downloadURL="$(downloadURLFromGit scriptingosx diskspace)"
appNewVersion="$(versionFromGit scriptingosx diskspace)"
expectedTeamID="JME5BW3F3R"
;;

View File

@@ -1,7 +1,7 @@
eshareosx) eshareosx)
name="e-Share" name="e-Share"
type="pkg" type="pkg"
packageID="com.ncryptedcloud.e-Share.pkg" #packageID="com.ncryptedcloud.e-Share.pkg"
downloadURL=https://www.ncryptedcloud.com/static/downloads/osx/$(curl -fs https://www.ncryptedcloud.com/static/downloads/osx/ | grep -o -i "href.*\".*\"" | cut -d '"' -f2) downloadURL=https://www.ncryptedcloud.com/static/downloads/osx/$(curl -fs https://www.ncryptedcloud.com/static/downloads/osx/ | grep -o -i "href.*\".*\"" | cut -d '"' -f2)
versionKey="CFBundleVersion" versionKey="CFBundleVersion"
appNewVersion=$( echo "${downloadURL}" | sed -E 's/.*\/[a-zA-Z\-]*_([0-9.]*)\.pkg/\1/g' ) appNewVersion=$( echo "${downloadURL}" | sed -E 's/.*\/[a-zA-Z\-]*_([0-9.]*)\.pkg/\1/g' )

View File

@@ -1,7 +1,7 @@
evernote) evernote)
name="Evernote" name="Evernote"
type="dmg" type="dmg"
downloadURL=$(curl -fs -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15)" "https://evernote.com/download" | grep -i ".dmg" | cut -d '"' -f2) downloadURL=$(curl -fs -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15)" "https://evernote.com/download" | grep -i ".dmg" | grep -ioe "href.*" | cut -d '"' -f2)
appNewVersion=$( echo "${downloadURL}" | sed -E 's/.*\/[a-zA-Z]*-([0-9.]*)-.*/\1/g' ) appNewVersion=$( echo "${downloadURL}" | sed -E 's/.*\/[a-zA-Z]*-([0-9.]*)-.*/\1/g' )
expectedTeamID="Q79WDW8YH9" expectedTeamID="Q79WDW8YH9"
appName="Evernote.app" appName="Evernote.app"

View File

@@ -1,10 +1,9 @@
golang) golang)
# credit: Søren Theilgaard (@theilgaard)
name="GoLang" name="GoLang"
type="pkg" type="pkg"
packageID="org.golang.go" packageID="org.golang.go"
downloadURL="$(curl -fsIL "https://golang.org$(curl -fs "https://golang.org/dl/" | grep -i "downloadBox" | grep "pkg" | tr '"' '\n' | grep "pkg")" | grep -i "^location" | awk '{print $2}' | tr -d '\r\n')" downloadURL="https://go.dev$(curl -fs "https://go.dev/dl/" | grep -i "downloadBox" | grep "pkg" | tr '"' '\n' | grep "pkg")"
appNewVersion="$( echo "${downloadURL}" | sed -E 's/.*\/(go[0-9.]*)\..*/\1/g' )" # Version includes letters "go" appNewVersion="$( echo "${downloadURL}" | sed -E 's/.*\/(go[0-9.]*)\..*/\1/g' )" # Version includes letters "go" in the beginning
expectedTeamID="EQHXZ8M8AV" expectedTeamID="EQHXZ8M8AV"
blockingProcesses=( NONE ) blockingProcesses=( NONE )
;; ;;

View File

@@ -1,8 +1,9 @@
logitechoptions) logitechoptions)
name="Logitech Options" name="Logitech Options"
type="pkgInZip" type="pkgInZip"
downloadURL=$(curl -fs https://support.logi.com/api/v2/help_center/en-us/articles.json | tr "," "\n" | grep -A 10 "macOS" | grep -oie "https.*/.*/options.*\.zip") #downloadURL=$(curl -fs "https://support.logi.com/api/v2/help_center/en-us/articles.json?label_names=webcontent=productdownload,webos=mac-macos-x-11.0" | tr "," "\n" | grep -A 10 "macOS" | grep -oie "https.*/.*/options/.*\.zip" | head -1)
appNewVersion=$(curl -fs https://support.logi.com/api/v2/help_center/en-us/articles.json | tr "," "\n" | grep -A 10 "macOS" | grep -B 5 -ie "https.*/.*/options.*\.zip" | grep "Software Version" | sed 's/\\u[0-9a-z][0-9a-z][0-9a-z][0-9a-z]//g' | grep -ioe "Software Version.*[0-9.]*" | tr "/" "\n" | grep -oe "[0-9.]*" | head -1) downloadURL="https://download01.logi.com/web/ftp/pub/techsupport/options/options_installer.zip"
appNewVersion=$(curl -fs "https://support.logi.com/api/v2/help_center/en-us/articles.json?label_names=webcontent=productdownload,webos=mac-macos-x-11.0" | tr "," "\n" | grep -A 10 "macOS" | grep -B 5 -ie "https.*/.*/options/.*\.zip" | grep "Software Version" | sed 's/\\u[0-9a-z][0-9a-z][0-9a-z][0-9a-z]//g' | grep -ioe "Software Version.*[0-9.]*" | tr "/" "\n" | grep -oe "[0-9.]*" | head -1)
#pkgName="LogiMgr Installer "*".app/Contents/Resources/LogiMgr.pkg" #pkgName="LogiMgr Installer "*".app/Contents/Resources/LogiMgr.pkg"
pkgName=LogiMgr.pkg pkgName=LogiMgr.pkg
expectedTeamID="QED4VVPZWA" expectedTeamID="QED4VVPZWA"

View File

@@ -3,7 +3,7 @@ mattermost)
type="dmg" type="dmg"
archiveName="mac-universal.dmg" archiveName="mac-universal.dmg"
downloadURL=$(downloadURLFromGit mattermost desktop) downloadURL=$(downloadURLFromGit mattermost desktop)
appNewVersion=$(versionFromGit mattermost desktop ) appNewVersion=$(versionFromGit mattermost desktop)
expectedTeamID="UQ8HT4Q2XM" expectedTeamID="UQ8HT4Q2XM"
Mattermost Helper (Renderer).app app.asar blockingProcesses=( "Mattermost Helper.app" "Mattermost Helper (Renderer).app" "Mattermost Helper (GPU).app" "Mattermost Helper (Plugin).app" )
;; ;;

View File

@@ -0,0 +1,16 @@
mochakeyboard)
name="Mocha Keyboard"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/mochakeyboard.dmg.zip"
curlOptions=( -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
-H "accept-encoding: gzip, deflate, br"
-H "accept-language: en-US,en;q=0.9"
-H "sec-fetch-dest: document"
-H "sec-fetch-mode: navigate"
-H "sec-fetch-user: ?1"
-H "sec-gpc: 1"
-H "upgrade-insecure-requests: 1" )
appNewVersion=""
expectedTeamID="RR9F5EPNVW"
;;

View File

@@ -0,0 +1,16 @@
mochatelnet)
name="Telnet"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/telnet.dmg.zip"
curlOptions=( -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
-H "accept-encoding: gzip, deflate, br"
-H "accept-language: en-US,en;q=0.9"
-H "sec-fetch-dest: document"
-H "sec-fetch-mode: navigate"
-H "sec-fetch-user: ?1"
-H "sec-gpc: 1"
-H "upgrade-insecure-requests: 1" )
appNewVersion=""
expectedTeamID="RR9F5EPNVW"
;;

View File

@@ -0,0 +1,16 @@
mochatn3270)
name="TN3270"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/tn3270.dmg.zip"
curlOptions=( -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
-H "accept-encoding: gzip, deflate, br"
-H "accept-language: en-US,en;q=0.9"
-H "sec-fetch-dest: document"
-H "sec-fetch-mode: navigate"
-H "sec-fetch-user: ?1"
-H "sec-gpc: 1"
-H "upgrade-insecure-requests: 1" )
appNewVersion=""
expectedTeamID="RR9F5EPNVW"
;;

View File

@@ -0,0 +1,16 @@
mochatn3812)
name="TN3812"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/tn3812.dmg.zip"
curlOptions=( -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
-H "accept-encoding: gzip, deflate, br"
-H "accept-language: en-US,en;q=0.9"
-H "sec-fetch-dest: document"
-H "sec-fetch-mode: navigate"
-H "sec-fetch-user: ?1"
-H "sec-gpc: 1"
-H "upgrade-insecure-requests: 1" )
appNewVersion=""
expectedTeamID="Frydendal"
;;

View File

@@ -0,0 +1,16 @@
mochatn5250)
name="TN5250"
type="appInDmgInZip"
downloadURL="https://mochasoft.dk/tn5250.dmg.zip"
curlOptions=( -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
-H "accept-encoding: gzip, deflate, br"
-H "accept-language: en-US,en;q=0.9"
-H "sec-fetch-dest: document"
-H "sec-fetch-mode: navigate"
-H "sec-fetch-user: ?1"
-H "sec-gpc: 1"
-H "upgrade-insecure-requests: 1" )
appNewVersion=""
expectedTeamID="RR9F5EPNVW"
;;

View File

@@ -1,3 +1,4 @@
promiseutility|\
promiseutilityr) promiseutilityr)
name="Promise Utility" name="Promise Utility"
type="pkgInDmg" type="pkgInDmg"

View File

@@ -2,6 +2,6 @@ tom4aconverter)
name="To M4A Converter" name="To M4A Converter"
type="dmg" type="dmg"
downloadURL="https://amvidia.com/downloads/to-m4a-converter-mac.dmg" downloadURL="https://amvidia.com/downloads/to-m4a-converter-mac.dmg"
appNewVersion=curl -sf "https://amvidia.com/to-m4a-converter" | grep -o -E '"softwareVersion":.'"{8}" | sed 's/\"//g' | awk -F ': ' '{print $2}' appNewVersion=$(curl -sf "https://amvidia.com/to-m4a-converter" | grep -o -E '"softwareVersion":.'"{8}" | sed 's/\"//g' | awk -F ': ' '{print $2}')
expectedTeamID="F2TH9XX9CJ" expectedTeamID="F2TH9XX9CJ"
;; ;;

View File

@@ -1,8 +1,8 @@
wacomdrivers) wacomdrivers)
name="Wacom Desktop Center" name="Wacom Desktop Center"
type="pkgInDmg" type="pkgInDmg"
downloadURL="$(curl -fs https://www.wacom.com/en-us/support/product-support/drivers | grep -e "drivers/mac/professional.*dmg" | head -1 | sed -e 's/data-download-link="//g' -e 's/"//' | awk '{$1=$1}{ print }' | sed 's/\r//')" downloadURL="$(curl -fs https://www.wacom.com/en-us/support/product-support/drivers | grep -e "drivers/mac/professional.*dmg" | head -1 | tr '"' "\n" | grep -i http)"
expectedTeamID="EG27766DY7" expectedTeamID="EG27766DY7"
pkgName="Install Wacom Tablet.pkg" #pkgName="Install Wacom Tablet.pkg"
appNewVersion="$(curl -fs https://www.wacom.com/en-us/support/product-support/drivers | grep mac/professional/releasenotes | head -1 | awk -F"|" '{print $1}' | awk -F"Driver" '{print $3}' | sed -e 's/ (.*//g' | tr -d ' ')" appNewVersion="$(curl -fs https://www.wacom.com/en-us/support/product-support/drivers | grep mac/professional/releasenotes | head -1 | tr '"' "\n" | grep -e "Driver [0-9][-0-9.]*" | sed -E 's/Driver ([-0-9.]*).*/\1/g')"
;; ;;

View File

@@ -2,6 +2,7 @@ wallyezflash)
name="Wally" name="Wally"
type="dmg" type="dmg"
downloadURL="https://configure.zsa.io/wally/osx" downloadURL="https://configure.zsa.io/wally/osx"
# 2022-02-07: Info.plist is totally wrong defined and contains no version information
#appNewVersion=$(curl -fsIL "${downloadURL}" | grep -i ^location | head -1 | sed -E 's/.*\/[a-zA-Z\-]*-([0-9.]*)\..*/\1/g') #appNewVersion=$(curl -fsIL "${downloadURL}" | grep -i ^location | head -1 | sed -E 's/.*\/[a-zA-Z\-]*-([0-9.]*)\..*/\1/g')
expectedTeamID="V32BWKSNYH" expectedTeamID="V32BWKSNYH"
#versionKey="CFBundleVersion" #versionKey="CFBundleVersion"

View File

@@ -15,8 +15,16 @@ fi
# MARK: application download and installation starts here # MARK: application download and installation starts here
if [[ ${INTERRUPT_DND} = "no" ]]; then
# Check if a fullscreen app is active
if hasDisplaySleepAssertion; then
cleanupAndExit 1 "active display sleep assertion detected, aborting"
fi
fi
printlog "BLOCKING_PROCESS_ACTION=${BLOCKING_PROCESS_ACTION}" printlog "BLOCKING_PROCESS_ACTION=${BLOCKING_PROCESS_ACTION}"
printlog "NOTIFY=${NOTIFY}" printlog "NOTIFY=${NOTIFY}"
printlog "LOGGING=${LOGGING}"
# Finding LOGO to use in dialogs # Finding LOGO to use in dialogs
case $LOGO in case $LOGO in
@@ -35,14 +43,17 @@ case $LOGO in
mosyleb) mosyleb)
# Mosyle Business # Mosyle Business
LOGO="/Applications/Self-Service.app/Contents/Resources/AppIcon.icns" LOGO="/Applications/Self-Service.app/Contents/Resources/AppIcon.icns"
if [[ -z $MDMProfileName ]]; then; MDMProfileName="Mosyle Corporation MDM"; fi
;; ;;
mosylem) mosylem)
# Mosyle Manager (education) # Mosyle Manager (education)
LOGO="/Applications/Manager.app/Contents/Resources/AppIcon.icns" LOGO="/Applications/Manager.app/Contents/Resources/AppIcon.icns"
if [[ -z $MDMProfileName ]]; then; MDMProfileName="Mosyle Corporation MDM"; fi
;; ;;
addigy) addigy)
# Addigy # Addigy
LOGO="/Library/Addigy/macmanage/MacManage.app/Contents/Resources/atom.icns" LOGO="/Library/Addigy/macmanage/MacManage.app/Contents/Resources/atom.icns"
if [[ -z $MDMProfileName ]]; then; MDMProfileName="MDM Profile"; fi
;; ;;
esac esac
if [[ ! -a "${LOGO}" ]]; then if [[ ! -a "${LOGO}" ]]; then
@@ -54,6 +65,8 @@ if [[ ! -a "${LOGO}" ]]; then
fi fi
printlog "LOGO=${LOGO}" printlog "LOGO=${LOGO}"
printlog "Label type: $type"
# MARK: extract info from data # MARK: extract info from data
if [ -z "$archiveName" ]; then if [ -z "$archiveName" ]; then
case $type in case $type in
@@ -74,6 +87,7 @@ if [ -z "$archiveName" ]; then
;; ;;
esac esac
fi fi
printlog "archiveName: $archiveName" DEBUG
if [ -z "$appName" ]; then if [ -z "$appName" ]; then
# when not given derive from name # when not given derive from name
@@ -112,7 +126,7 @@ else
fi fi
# MARK: change directory to temporary working directory # MARK: change directory to temporary working directory
printlog "Changing directory to $tmpDir" printlog "Changing directory to $tmpDir" DEBUG
if ! cd "$tmpDir"; then if ! cd "$tmpDir"; then
printlog "error changing directory $tmpDir" printlog "error changing directory $tmpDir"
cleanupAndExit 1 cleanupAndExit 1
@@ -168,8 +182,8 @@ fi
if [ -f "$archiveName" ] && [ "$DEBUG" -eq 1 ]; then if [ -f "$archiveName" ] && [ "$DEBUG" -eq 1 ]; then
printlog "$archiveName exists and DEBUG mode 1 enabled, skipping download" printlog "$archiveName exists and DEBUG mode 1 enabled, skipping download"
else else
# download the dmg # download
printlog "Downloading $downloadURL to $archiveName" printlog "Downloading $downloadURL to $archiveName" REQ
if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then
printlog "notifying" printlog "notifying"
if [[ $updateDetected == "YES" ]]; then if [[ $updateDetected == "YES" ]]; then
@@ -178,19 +192,28 @@ else
displaynotification "Downloading new $name" "Download in progress …" displaynotification "Downloading new $name" "Download in progress …"
fi fi
fi fi
if ! curl --location --fail --silent "$downloadURL" -o "$archiveName"; then curlDownload=$(curl -v -fsL --show-error ${curlOptions} "$downloadURL" -o "$archiveName" 2>&1)
curlDownloadStatus=$(echo $?)
deduplicatelogs "$curlDownload"
if [[ $curlDownloadStatus -ne 0 ]]; then
#if ! curl --location --fail --silent "$downloadURL" -o "$archiveName"; then
printlog "error downloading $downloadURL" printlog "error downloading $downloadURL"
message="$name update/installation failed. This will be logged, so IT can follow up." message="$name update/installation failed. This will be logged, so IT can follow up."
if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then
printlog "notifying" printlog "notifying"
if [[ $updateDetected == "YES" ]]; then if [[ $updateDetected == "YES" ]]; then
displaynotification "$message" "Error updating $name" displaynotification "$message" "Error updating $name" ERROR
else else
displaynotification "$message" "Error installing $name" displaynotification "$message" "Error installing $name" ERROR
fi fi
fi fi
cleanupAndExit 2 printlog "File list: $(ls -lh "$archiveName")" ERROR
printlog "File type: $(file "$archiveName")" ERROR
cleanupAndExit 2 "Error downloading $downloadURL error:\n$logoutput" ERROR
fi fi
printlog "File list: $(ls -lh "$archiveName")" DEBUG
printlog "File type: $(file "$archiveName")" DEBUG
printlog "curl output was:\n$logoutput" DEBUG
fi fi
# MARK: when user is logged in, and app is running, prompt user to quit app # MARK: when user is logged in, and app is running, prompt user to quit app
@@ -207,7 +230,7 @@ else
fi fi
# MARK: install the download # MARK: install the download
printlog "Installing $name" printlog "Installing $name" REQ
if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then if [[ $currentUser != "loginwindow" && $NOTIFY == "all" ]]; then
printlog "notifying" printlog "notifying"
if [[ $updateDetected == "YES" ]]; then if [[ $updateDetected == "YES" ]]; then
@@ -219,7 +242,7 @@ fi
if [ -n "$installerTool" ]; then if [ -n "$installerTool" ]; then
# installerTool defined, and we use that for installation # installerTool defined, and we use that for installation
printlog "installerTool used: $installerTool" printlog "installerTool used: $installerTool" REQ
appName="$installerTool" appName="$installerTool"
fi fi

View File

@@ -153,32 +153,105 @@ echo "Downloading $downloadURL"
echo $(basename $downloadURL) echo $(basename $downloadURL)
# First trying to find redirection headers on the download, as those can contain version numbers # First trying to find redirection headers on the download, as those can contain version numbers
echo "Redirecting to (maybe this can help us with version):\n$(curl -fsIL -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15" -H "accept-encoding: gzip, deflate, br" -H "Referrer Policy: strict-origin-when-cross-origin" -H "upgrade-insecure-requests: 1" -H "sec-fetch-dest: document" -H "sec-gpc: 1" -H "sec-fetch-user: ?1" -H "accept-language: en-US,en;q=0.9" -H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" -H "sec-fetch-mode: navigate" "$downloadURL" | grep -i "^[location|x\-amz\-meta\-version]*")" echo "Redirecting to (maybe this can help us with version):\n$(curl -fsIL -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15" -H "accept-encoding: gzip, deflate, br" -H "Referrer Policy: strict-origin-when-cross-origin" -H "upgrade-insecure-requests: 1" -H "sec-fetch-dest: document" -H "sec-gpc: 1" -H "sec-fetch-user: ?1" -H "accept-language: en-US,en;q=0.9" -H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" -H "sec-fetch-mode: navigate" "$downloadURL" | grep -i "^[location|x\-amz\-meta\-version]*")"
# Now downloading without extra headers # Now downloading without various sets of extra headers
if ! downloadOut="$(curl -fL "$downloadURL" --remote-header-name --remote-name -w "%{filename_effective}\n%{url_effective}\n")"; then if ! downloadOut1="$( \
echo "error downloading $downloadURL using standard headers." curl -fL "$downloadURL" --remote-header-name --remote-name \
echo "result: $downloadOut" -w "%{filename_effective}\n%{url_effective}\n")"
echo "Trying all headers…" # that I know of then
if ! downloadOut="$(curl -fL -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15" -H "accept-encoding: gzip, deflate, br" -H "Referrer Policy: strict-origin-when-cross-origin" -H "upgrade-insecure-requests: 1" -H "sec-fetch-dest: document" -H "sec-gpc: 1" -H "sec-fetch-user: ?1" -H "accept-language: en-US,en;q=0.9" -H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" -H "sec-fetch-mode: navigate" "$downloadURL" --remote-header-name --remote-name -w "%{filename_effective}\n%{url_effective}\n")"; then echo "error downloading $downloadURL with no headers."
echo "Trying almost all headers…" # that I know of echo "result: $downloadOut1"
if ! downloadOut="$(curl -fL -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15" -H "accept-encoding: gzip, deflate, br" -H "upgrade-insecure-requests: 1" -H "sec-fetch-dest: document" -H "sec-gpc: 1" -H "sec-fetch-user: ?1" -H "accept-language: en-US,en;q=0.9" -H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" -H "sec-fetch-mode: navigate" "$downloadURL" --remote-header-name --remote-name -w "%{filename_effective}\n%{url_effective}\n")"; then echo "Trying 1st set of extra headers to download."
# we are only here if the download failed if ! downloadOut2="$( \
echo "error downloading $downloadURL using two different sets of headers." curl -fL \
echo "result: $downloadOut" -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15" \
# Sometimes a server will give some results to the downloaded output -H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" \
if [[ -n $downloadOut ]]; then -H "accept-encoding: gzip, deflate, br" \
echo "Trying output of this…" -H "accept-language: en-US,en;q=0.9" \
downloadURL="$(echo $downloadOut | tail -1)" -H "sec-fetch-dest: document" \
# Last chance for succes on this download -H "sec-fetch-mode: navigate" \
if ! downloadOut="$(curl -fL "$downloadURL" --remote-header-name --remote-name -w "%{filename_effective}\n%{url_effective}\n")"; then -H "sec-fetch-user: ?1" \
echo "error downloading $downloadURL using previous output." -H "sec-gpc: 1" \
echo "result: $downloadOut" -H "upgrade-insecure-requests: 1" \
"$downloadURL" --remote-header-name --remote-name \
-w "%{filename_effective}\n%{url_effective}\n")"
then
echo "error downloading $downloadURL with 1st set of headers."
echo "result: $downloadOut2"
echo "Trying 2nd set of extra headers to download."
if ! downloadOut3="$( \
curl -fL \
-H "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36" \
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" \
-H "accept-encoding: gzip, deflate, br" \
-H "accept-language: en-US,en;q=0.9" \
-H "sec-fetch-dest: document" \
-H "sec-fetch-mode: navigate" \
-H "sec-fetch-site: same-site" \
-H "sec-fetch-user: ?1" \
-H "sec-gpc: 1" \
-H "upgrade-insecure-requests: 1" \
"$downloadURL" --remote-header-name --remote-name \
-w "%{filename_effective}\n%{url_effective}\n")"
then
echo "error downloading $downloadURL with 2nd set of headers."
echo "result: $downloadOut3"
echo "Trying 3rd set of extra headers to download."
if ! downloadOut4="$( \
curl -fL \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15" \
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" \
-H "accept-encoding: gzip, deflate, br" \
-H "accept-language: en-US,en;q=0.9" \
-H "sec-fetch-dest: document" \
-H "sec-fetch-mode: navigate" \
-H "sec-fetch-user: ?1" \
-H "sec-gpc: 1" \
-H "upgrade-insecure-requests: 1" \
-H "Referrer Policy: strict-origin-when-cross-origin" \
"$downloadURL" --remote-header-name --remote-name \
-w "%{filename_effective}\n%{url_effective}\n")"
then
# we are only here if the download failed
echo "error downloading $downloadURL with 3rd set of headers."
echo "result: $downloadOut4"
echo "no more header sets to try"
# Sometimes a server will give some results to the downloaded output
echo "If any information came out of the previous download attempts, we can try those…"
downloadOuts=( "$downloadOut1" "$downloadOut3" "$downloadOut3" "$downloadOut4" )
downloadOutCount=${#downloadOuts}
for downloadOut in $downloadOuts ; do
if [[ -n $downloadOut ]]; then
echo "Trying output of this…"
downloadURL="$(echo $downloadOut | tail -1)"
# Last chance for succes on this download
if ! downloadOut="$(curl -fL "$downloadURL" --remote-header-name --remote-name -w "%{filename_effective}\n%{url_effective}\n")"; then
echo "error downloading $downloadURL using previous output."
echo "result: $downloadOut"
((downloadOutCount--))
else
echo "Success on this download."
succesDownloadOut=$downloadOut
break 2
fi
fi
done
if [[ $downloadOutCount -eq 0 ]]; then
echo "No more tries. Cannot continue." echo "No more tries. Cannot continue."
exit 1 exit 1
fi fi
else
succesDownloadOut=$downloadOut4
fi fi
else
succesDownloadOut=$downloadOut3
fi fi
else
succesDownloadOut=$downloadOut2
fi fi
else
succesDownloadOut=$downloadOut1
fi fi
downloadOut=$succesDownloadOut
# Now we have downloaded the archive, and we need to analyze this # Now we have downloaded the archive, and we need to analyze this
# The download have returned both {filename_effective} and {url_effective} # The download have returned both {filename_effective} and {url_effective}
@@ -192,7 +265,7 @@ echo "archivePath: $archivePath"
# So we want to investigate which one has the filename # So we want to investigate which one has the filename
try1archiveName=${${archiveTempName##*/}%%\?*} try1archiveName=${${archiveTempName##*/}%%\?*}
try2archiveName=${${archivePath##*/}%%\?*} try2archiveName=${${archivePath##*/}%%\?*}
fileName_re='^([a-zA-Z0-9\_.%-]*)\.(dmg|pkg|zip|tbz)$' # regular expression for matching fileName_re='^([a-zA-Z0-9\_.%-]*)\.(dmg|pkg|zip|tbz|gz)$' # regular expression for matching
if [[ "${try1archiveName}" =~ $fileName_re ]]; then if [[ "${try1archiveName}" =~ $fileName_re ]]; then
archiveName=${try1archiveName} archiveName=${try1archiveName}
elif [[ "${try2archiveName}" =~ $fileName_re ]]; then elif [[ "${try2archiveName}" =~ $fileName_re ]]; then
@@ -251,7 +324,7 @@ fi
identifier=${name:l} # making lower case identifier=${name:l} # making lower case
identifier=${identifier//\%[0-9a-fA-F][0-9a-fA-F]} # removing certain characters identifier=${identifier//\%[0-9a-fA-F][0-9a-fA-F]} # removing certain characters
identifier=${identifier//[,._*@$\(\)\-]} # removing more characters from label name identifier=${identifier//[ ,._*@$\(\)\-]} # removing more characters from label name
echo "identifier: $identifier" echo "identifier: $identifier"
# github-part to figure out if we can find author and repo, to use our github functions for the label # github-part to figure out if we can find author and repo, to use our github functions for the label

188
utils/checkInstallomator.sh Executable file
View File

@@ -0,0 +1,188 @@
#!/bin/zsh
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
# Check Installomator with various labels in various modes
# 2022 Søren Theilgaard (@theilgaard)
# This script will use various labels to check if Installomator is working as it is supposed to do
# To check this script use these labels:
# desktoppr dbeaverce brave microsoftteams whatsapp citrixworkspace aircall devonthink
# MARK: Constants
# Labels to test in DEBUG=2 mode
allLabels=( dbeaverce signal malwarebytes mochatn3270 logitechoptions googlechrome brave macports inkscape devonthink omnidisksweeper microsoftteams applenyfonts sketch sqlpropostgres desktoppr marathon)
# Labels to test for real (script use sudo to ask for admin rights)
# Purpose is only toest things that are being skipped in DEBUG mode
allLabelsArg=(
"vlc"
"depnotify NOTIFY=all"
"brave NOTIFY=silent"
"dialog"
"handbrake SYSTEMOWNER=1"
)
## Testing for combinations of these
# Label types: dmg, pkg, zip, tbz, pkgInDmg, pkgInZip, appInDmgInZip
# Label fields: packageID, appNewVersion, versionKey, appCustomVersion(){}, archiveName, appName, pkgName
# dbeaverse: dmg without appNewVersion and does not have LSMinimumSystemVersion in Info.plist
# signal: dmg with appNewVersion
# malwarebytes: pkg with appNewVersion but not packageID
# mochatn3270: appInDmgInZip with curlOptions
# logitechoptions pkgInZip with pkgName but without packageID
# googlechrome: dmg with appNewVersion
# brave: dmg with appNewVersion from versionKey
# macports: with custom code for archiveName, and with appNewVersion and appCustomVersion
# inkscape: dmg with appCustomVersion
# devonthink: appInDmgInZip
# omnidisksweeper: with appNewVersion, and uses xpath
# microsoftteams: pkg with appNewVersion from packageID
# applenyfonts: pkgInDmg from Apple with packageID and no appNewVersion
# sketch: zip with appNewVersion
# sqlpropostgres: zip without appNewVersion
# desktoppr: pkg from github with packageID
# marathon: dmg from github with archiveName
# Label types not possible to test in DEBUG mode: updateronly
# Label fields not possible to test in DEBUG mode: targetDir, blockingProcesses, updateTool, updateToolRunAsCurrentUser, installerTool, CLIInstaller, CLIArguments
# Labels tested for real
# vlc: app-copy
# depnotify: pkg-install without appNewVersion
# brave: app-copy but with few extras
# dialog: pkg from GitHub
# handbrake: app-copy
# adobecreativeclouddesktop: dmg with appNewVersion and installerTool, CLIInstaller, CLIArguments
#setup some folders
script_dir=$(dirname ${0:A})
repo_dir=$(dirname $script_dir)
build_dir="$repo_dir/build"
destination_file="$build_dir/Installomator.sh"
fragments_dir="$repo_dir/fragments"
labels_dir="$fragments_dir/labels"
# MARK: Script
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
NC='\033[0m' # No Color
# Check minimal macOS requirement
if [[ $(sw_vers -buildVersion ) < "18" ]]; then
echo "Installomator requires at least macOS 10.14 Mojave."
exit 98
fi
echo "TESTING Installoamator"
echo "Version: $($repo_dir/assemble.sh version)"
echo "\nRemember to follow log in another terminal window (for the REAL tests):"
echo "tail -f /var/log/Installomator.log\n"
currentUser=$(stat -f "%Su" /dev/console)
warningLabels="" # variable for labels with warnings
errorLabels="" # variable for labels with errors
countWarning=0
countError=0
checkCmd_output() {
#echo "$cmd_output"
no_appNewVersion=$( echo "$cmd_output" | grep --binary-files=text -ic "Latest version not specified." )
echo "No appNewVersion: $no_appNewVersion (1 for no)"
latest_appNewVersion=$( echo "$cmd_output" | grep --binary-files=text -i "Latest version of " | sed -E 's/.* is ([0-9.]*),*.*$/\1/g' )
echo "Latest version: $latest_appNewVersion"
github_label=$( echo "$cmd_output" | grep --binary-files=text -ci "Downloading https://github.com" )
echo "GitHub: $github_label (1 for true)"
downloaded_version=$( echo "$cmd_output" | grep --binary-files=text -ioE "Downloaded (package.*version|version of.*is) [0-9.]*" | grep -v "is the same as installed" | sed -E 's/.* (is|version) ([0-9.]*).*/\2/g' )
echo "Downloaded version: $downloaded_version"
exit_status=$( echo "$cmd_output" | grep --binary-files=text exit | tail -1 | sed -E 's/.*exit code ([0-9]).*/\1/g' )
echo "Exit: $exit_status"
if [[ ${exit_status} -eq 0 ]] ; then
if [[ $no_appNewVersion -eq 1 ]]; then
echo "${GREEN}$label works fine, but no appNewVersion.${NC}"
elif [[ $latest_appNewVersion == $downloaded_version && $github_label -eq 0 ]]; then
echo "${GREEN}$label works fine, with version $latest_appNewVersion.${NC}"
elif [[ $github_label -eq 1 ]]; then
echo "${GREEN}$label works fine, with GitHub version $latest_appNewVersion.${NC}"
elif [[ $downloaded_version == "" ]]; then
echo "${GREEN}$label works fine, but downloaded version can not be checked without packageID.${NC}"
elif [[ $latest_appNewVersion != $downloaded_version && $github_label -eq 0 ]]; then
echo "${YELLOW}$label has version warning, with latest $latest_appNewVersion not matching downloaded $downloaded_version.${NC}"
((countWarning++))
warningLabels+=( "$label" )
echo "$cmd_output"
else
echo "${RED}$label NOT WORKING:${NC}"
((countError++))
errorLabels+=( "$label" )
echo "$cmd_output"
fi
else
echo "${RED}$label FAILED with exit code ${exit_status}:${NC}"
((countError++))
errorLabels+=( "$label" )
echo "$cmd_output"
fi
}
# Mark: First part in DEBUG=2 mode
for label in $allLabels; do
label_name=$( $repo_dir/assemble.sh $label DEBUG=2 RETURN_LABEL_NAME=1 | tail -1 )
if [[ "$label_name" == "#" ]]; then
echo "${RED}Label $label does not exist. Skipping.${NC}"
else
echo "Label $label: $label_name"
cmd_output=$( $repo_dir/assemble.sh $label DEBUG=2 INSTALL=force IGNORE_APP_STORE_APPS=yes BLOCKING_PROCESS_ACTION=ignore )
#echo "$cmd_output"
checkCmd_output
echo
fi
done
# Mark: Testing for real
echo "\nTesting for REAL:\n"
for labelArg in $allLabelsArg; do
echo $labelArg
label=$(echo $labelArg | cut -d" " -f1 )
arg1=$(echo $labelArg | cut -d" " -f2 )
arg2=$(echo $labelArg | cut -d" " -f3 )
label_name=$( $repo_dir/assemble.sh $label DEBUG=2 RETURN_LABEL_NAME=1 | tail -1 )
if [[ "$label_name" == "#" ]]; then
echo "${RED}Label $label does not exist. Skipping.${NC}"
else
echo "Label $label: $label_name"
cmd_output=$( sudo $repo_dir/assemble.sh $label $arg1 $arg2 DEBUG=0 LOGGING=DEBUG INSTALL=force BLOCKING_PROCESS_ACTION=quit )
#echo "$cmd_output"
argument_variables=$( echo "$cmd_output" | grep --binary-files=text -i "setting variable from argument" | sed -E 's/.*setting variable from argument (.*)$/\1/g')
echo $argument_variables
checkCmd_output
echo
fi
done
echo
if [[ countWarning -gt 0 ]]; then
echo "${YELLOW}Warnings counted: $countWarning${NC}"
echo "${YELLOW}${warningLabels}${NC}"
else
echo "${GREEN}No warnings detected!${NC}"
fi
if [[ countError -gt 0 ]]; then
echo "${RED}ERRORS counted: $countError${NC}"
echo "${YELLOW}${errorLabels}${NC}"
else
echo "${GREEN}No errors detected!${NC}"
fi
echo "Done!"

View File

@@ -3,7 +3,7 @@
export PATH=/usr/bin:/bin:/usr/sbin:/sbin export PATH=/usr/bin:/bin:/usr/sbin:/sbin
# Check Installomator labels from fragments # Check Installomator labels from fragments
# 2021 Søren Theilgaard (@theilgaard) # 2021-2022 Søren Theilgaard (@theilgaard)
# This script will test labels and check if download link is active, and if version is defined. # This script will test labels and check if download link is active, and if version is defined.
# If labels are written to the script only those will be tested. # If labels are written to the script only those will be tested.
@@ -89,6 +89,45 @@ arch () {
echo $fixedArch echo $fixedArch
} }
checkCmd_output() {
#echo "$cmd_output"
no_appNewVersion=$( echo "$cmd_output" | grep --binary-files=text -ic "Latest version not specified." )
echo "No appNewVersion: $no_appNewVersion (1 for no)"
latest_appNewVersion=$( echo "$cmd_output" | grep --binary-files=text -i "Latest version of " | sed -E 's/.* is ([0-9.]*),*.*$/\1/g' )
echo "Latest version: $latest_appNewVersion"
github_label=$( echo "$cmd_output" | grep --binary-files=text -ci "Downloading https://github.com" )
echo "GitHub: $github_label (1 for true)"
downloaded_version=$( echo "$cmd_output" | grep --binary-files=text -ioE "Downloaded (package.*version|version of.*is) [0-9.]*" | grep -v "is the same as installed" | sed -E 's/.* (is|version) ([0-9.]*).*/\2/g' )
echo "Downloaded version: $downloaded_version"
exit_status=$( echo "$cmd_output" | grep --binary-files=text exit | tail -1 | sed -E 's/.*exit code ([0-9]).*/\1/g' )
echo "Exit: $exit_status"
if [[ ${exit_status} -eq 0 ]] ; then
if [[ $no_appNewVersion -eq 1 ]]; then
echo "${GREEN}$label works fine, but no appNewVersion.${NC}"
elif [[ $latest_appNewVersion == $downloaded_version && $github_label -eq 0 ]]; then
echo "${GREEN}$label works fine, with version $latest_appNewVersion.${NC}"
elif [[ $github_label -eq 1 ]]; then
echo "${GREEN}$label works fine, with GitHub version $latest_appNewVersion.${NC}"
elif [[ $downloaded_version == "" ]]; then
echo "${GREEN}$label works fine, but downloaded version can not be checked without packageID.${NC}"
elif [[ $latest_appNewVersion != $downloaded_version && $github_label -eq 0 ]]; then
echo "${YELLOW}$label has version warning, with latest $latest_appNewVersion not matching downloaded $downloaded_version.${NC}"
((countWarning++))
warningLabels+=( "$label" )
echo "$cmd_output"
else
echo "${RED}$label NOT WORKING:${NC}"
((countError++))
errorLabels+=( "$label" )
echo "$cmd_output"
fi
else
echo "${RED}$label FAILED with exit code ${exit_status}:${NC}"
((countError++))
errorLabels+=( "$label" )
echo "$cmd_output"
fi
}
# MARK: Script # MARK: Script
RED='\033[0;31m' RED='\033[0;31m'
@@ -97,132 +136,164 @@ YELLOW='\033[1;33m'
BLUE='\033[1;34m' BLUE='\033[1;34m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
# Labels with the $(arch) call for different versions for Intel and Apple Silicon should be listed here: # Has label(s) been given as arguments or not, and list those
archLabels=( $(grep "\$(arch)" ${labels_dir}/* | awk '{print $1}' | sed -E 's/.*\/([a-z0-9\_-]*)\..*/\1/g'| uniq ) ) # Figure out which ones of these include "$(arch)" so those will be testet for both i386 and arm64 architectures
echo "${BLUE}Labels with \"\$(arch)\" call:${NC}\n${archLabels}\n"
if [[ $# -eq 0 ]]; then if [[ $# -eq 0 ]]; then
allLabels=( $(grep -h -E '^([a-z0-9\_-]*)(\)|\|\\)$' ${labels_dir}/*.sh | tr -d ')|\\' | sort) ) allLabels=( $(grep -h -E '^([a-z0-9\_-]*)(\)|\|\\)$' ${labels_dir}/*.sh | tr -d ')|\\' | sort) )
archLabels=( $(grep "\$(arch)" ${labels_dir}/* | awk '{print $1}' | sed -E 's/.*\/([a-z0-9\_-]*)\..*/\1/g'| uniq ) )
else else
allLabels=( ${=@} ) allLabels=( ${=@} )
# Check if labels exist
for checkLabel in $allLabels; do
if [ ! $(ls ${labels_dir}/${checkLabel}.sh 2>/dev/null) ] ; then
# Remove label from array
allLabels=("${(@)allLabels:#$checkLabel}")
fi
done
# Figure out if labels has "$(arch)" in them
archLabels=( $allLabels )
for checkLabel in $archLabels; do
if [ ! -n "$(grep "\$(arch)" ${labels_dir}/${checkLabel}.sh 2>/dev/null)" ] ; then
# Remove label from array
archLabels=("${(@)archLabels:#$checkLabel}")
fi
done
fi fi
echo "${BLUE}Total labels:${NC}\n${allLabels}\n" echo "${BLUE}Total labels:${NC}\n${allLabels}\n"
echo "${BLUE}Labels with \"\$(arch)\" call:${NC}\n${archLabels}\n"
secondRoundLabels="" # variable for labels with $(arch) call in them secondRoundLabels="" # variable for labels with $(arch) call in them
warningLabels="" # variable for labels with warnings
errorLabels="" # variable for labels with errors
countWarning=0 countWarning=0
countError=0 countError=0
# Loop through the 2 architectures
for fixedArch in i386 arm64; do for fixedArch in i386 arm64; do
echo "${BLUE}Architecture: $fixedArch${NC}" echo "${BLUE}Architecture: $fixedArch${NC}"
echo echo
# Go through all labels # Loop through all labels
for label in $allLabels; do for label in $allLabels; do
echo "########## $label" echo "########## $label"
labelWarning=0; labelError=0; expectedExtension=""; URLextension="" labelWarning=0; labelError=0; expectedExtension=""; URLextension=""
name=""; type=""; downloadURL=""; appNewVersion=""; expectedTeamID=""; blockingProcesses=""; updateTool=""; updateToolArguments=""; archiveName="" name=""; type=""; downloadURL=""; curlOptions=""; appNewVersion=""; expectedTeamID=""; blockingProcesses=""; updateTool=""; updateToolArguments=""; archiveName=""
#caseLabel #caseLabel
if cat "${labels_dir}/${label}.sh" | grep -v -E '^[a-z0-9\_-]*(\)|\|\\)$' | grep -v ";;" > checkLabelCurrent.sh; then if cat "${labels_dir}/${label}.sh" | grep -v -E '^[a-z0-9\_-]*(\)|\|\\)$' | grep -v ";;" > checkLabelCurrent.sh; then
source checkLabelCurrent.sh source checkLabelCurrent.sh
echo "Name: $name" echo "Name: $name"
echo "Download URL: $downloadURL" echo "Download URL: $downloadURL"
echo "Type: $type" echo "Type: $type"
case $type in case $type in
dmg|pkg|zip|tbz) dmg|pkg|zip|tbz)
expectedExtension="$type" expectedExtension="$type"
;; ;;
pkgInDmg) pkgInDmg)
expectedExtension="dmg" expectedExtension="dmg"
;; ;;
*InZip) *InZip)
expectedExtension="zip" expectedExtension="zip"
;; ;;
*) *)
echo "Cannot handle type $type" echo "Cannot handle type $type"
;; ;;
esac esac
if [[ "$appNewVersion" == "" ]] ; then if [[ "$appNewVersion" == "" ]] ; then
echo "No appNewVersion!" echo "No appNewVersion!"
else
if [[ $( echo "$appNewVersion" | grep -i "[0-9.]" ) == "" || $appNewVersion == "" ]]; then
echo "${RED}-> !! ERROR in appNewVersion${NC}"
labelError=1
else else
if [[ $appNewVersion != $( echo "$appNewVersion" | sed -E 's/[^0-9]*([0-9.]*)[^0-9]*/\1/g' ) ]]; then if [[ $( echo "$appNewVersion" | grep -i "[0-9.]" ) == "" || $appNewVersion == "" ]]; then
echo "${YELLOW}Warning: Version contain not only numbers and dots.${NC}" echo "${RED}-> !! ERROR in appNewVersion${NC}"
labelWarning=1 labelError=1
else
if [[ $appNewVersion != $( echo "$appNewVersion" | sed -E 's/[^0-9]*([0-9.]*)[^0-9]*/\1/g' ) ]]; then
echo "${YELLOW}Warning: Version contain not only numbers and dots.${NC}"
labelWarning=1
fi
echo "Version: $appNewVersion" ;
fi fi
echo "Version: $appNewVersion" ;
fi fi
fi if curl -sfL ${curlOptions} --output /dev/null -r 0-0 "$downloadURL" ; then
if curl -sfL --output /dev/null -r 0-0 "$downloadURL" ; then echo "${GREEN}OK: downloadURL works OK${NC}"
echo "${GREEN}OK: downloadURL works OK${NC}" if [[ $(echo "$downloadURL" | sed -E 's/.*\.([a-zA-Z]*)\s*/\1/g' ) == "${expectedExtension}" ]]; then
if [[ $(echo "$downloadURL" | sed -E 's/.*\.([a-zA-Z]*)\s*/\1/g' ) == "${expectedExtension}" ]]; then echo "${GREEN}OK: download extension MATCH on ${expectedExtension}${NC}"
echo "${GREEN}OK: download extension MATCH on ${expectedExtension}${NC}" else
else if [[ $(echo "$downloadURL" | grep -io "github.com") != "github.com" ]]; then
if [[ $(echo "$downloadURL" | grep -io "github.com") != "github.com" ]]; then URLheader=$( curl -fsIL "$downloadURL" )
URLheader=$( curl -fsIL "$downloadURL" ) if [[ "${URLheader}" != "" ]]; then
if [[ "${URLheader}" != "" ]]; then URLlocation=$( echo "${URLheader}" | grep -i "^location" )
URLlocation=$( echo "${URLheader}" | grep -i "^location" ) URLfilename=$( echo "${URLheader}" | grep -i "filename=" )
URLfilename=$( echo "${URLheader}" | grep -i "filename=" ) if [[ "${URLlocation}" != "" ]]; then
if [[ "${URLlocation}" != "" ]]; then URLextension=$( echo "${URLlocation}" | tail -1 | sed -E 's/.*\.([a-zA-Z]*)\s*/\1/g' | tr -d '\r\n' )
URLextension=$( echo "${URLlocation}" | tail -1 | sed -E 's/.*\.([a-zA-Z]*)\s*/\1/g' | tr -d '\r\n' ) else
URLextension=$( echo "${URLfilename}" | tail -1 | sed -E 's/.*\.([a-zA-Z]*)\s*/\1/g' | tr -d '\r\n' )
fi
URLextension=${${URLextension:l}%%\?*}
if [[ "${URLextension}" == "${expectedExtension}" ]]; then
echo "${GREEN}OK: download extension MATCH on ${URLextension}${NC}"
else
echo "${RED}-> !! ERROR in download extension, expected ${expectedExtension}, but got ${URLextension}.${NC}"
labelError=1
fi
else else
URLextension=$( echo "${URLfilename}" | tail -1 | sed -E 's/.*\.([a-zA-Z]*)\s*/\1/g' | tr -d '\r\n' ) echo "no header provided from server."
fi fi
URLextension=${${URLextension:l}%%\?*} else
if [[ "${URLextension}" == "${expectedExtension}" ]]; then githubPart="$(echo "$downloadURL" | cut -d "/" -f4-6)"
echo "${GREEN}OK: download extension MATCH on ${URLextension}${NC}" if [[ "$(curl -fsL "$downloadURL" | grep -io "${githubPart}.*\.${expectedExtension}")" != "" ]]; then
echo "${GREEN}OK: download extension MATCH on ${expectedExtension}${NC}"
else else
echo "${RED}-> !! ERROR in download extension, expected ${expectedExtension}, but got ${URLextension}.${NC}" echo "${RED}-> !! ERROR in download extension, expected ${expectedExtension}, but it was wrong${NC}"
labelError=1 labelError=1
fi fi
else
echo "no header provided from server."
fi
else
githubPart="$(echo "$downloadURL" | cut -d "/" -f4-6)"
if [[ "$(curl -fsL "$downloadURL" | grep -io "${githubPart}.*\.${expectedExtension}")" != "" ]]; then
echo "${GREEN}OK: download extension MATCH on ${expectedExtension}${NC}"
else
echo "${RED}-> !! ERROR in download extension, expected ${expectedExtension}, but it was wrong${NC}"
labelError=1
fi fi
fi fi
else
echo "${RED}-> !! ERROR in downloadURL${NC}"
labelError=1
fi
if [[ $labelError != 0 || $labelWarning != 0 ]]; then
echo "${RED}########## ERROR in label: $label${NC}"
echo "Testing using Installomator"
#exit_status=$( . $repo_dir/assemble.sh $label DEBUG=2 INSTALL=force IGNORE_APP_STORE_APPS=yes BLOCKING_PROCESS_ACTION=ignore | grep exit | tail -1 | sed -E 's/.*exit code ([0-9]).*/\1/g' )
cmd_output=$( $repo_dir/assemble.sh $label DEBUG=2 INSTALL=force IGNORE_APP_STORE_APPS=yes BLOCKING_PROCESS_ACTION=ignore )
#echo "$cmd_output"
checkCmd_output
fi
if (($archLabels[(Ie)$label])); then
secondRoundLabels+=( "$label" )
fi fi
else else
echo "${RED}-> !! ERROR in downloadURL${NC}" echo "Label: ${label} is not it's own file in Labels-folder. Skipping"
labelError=1
fi
if [[ $labelWarning != 0 ]]; then; echo "${YELLOW}########## Warning in label: $label${NC}"; ((countWarning++)); fi
if [[ $labelError != 0 ]]; then; echo "${RED}########## ERROR in label: $label${NC}"; ((countError++)); fi
if (($archLabels[(Ie)$label])); then
secondRoundLabels+=( "$label" )
fi fi
echo
done
if [[ $fixedArch == i386 ]] ; then
errorLabelsi386=( ${=errorLabels} )
else else
echo "Label: ${label} is not it's own file in Labels-folder. Skipping" errorLabelsarm64=( ${=errorLabels} )
fi fi
errorLabels=""
allLabels=( ${=secondRoundLabels} )
archLabels=()
echo echo
done done
allLabels=( ${=secondRoundLabels} )
archLabels=()
echo
done
rm checkLabelCurrent.sh rm checkLabelCurrent.sh
#${SELFLOCATION}/Installomator.sh version if [[ countWarning -gt 0 ]]; then
#echo
if [[ countWarning > 0 ]]; then
echo "${YELLOW}Warnings counted: $countWarning${NC}" echo "${YELLOW}Warnings counted: $countWarning${NC}"
echo "${YELLOW}${warningLabels}${NC}"
else else
echo "${GREEN}No warnings detected!${NC}" echo "${GREEN}No warnings detected!${NC}"
fi fi
if [[ countError > 0 ]]; then if [[ countError -gt 0 ]]; then
echo "${RED}ERRORS counted: $countError${NC}" echo "${RED}ERRORS counted: $countError${NC}"
echo "${RED}i386 : ${errorLabelsi386}${NC}"
echo "${RED}arm64: ${errorLabelsarm64}${NC}"
else else
echo "${GREEN}No errors detected!${NC}" echo "${GREEN}No errors detected!${NC}"
fi fi