├── .gitignore
├── README.md
├── build-info.plist
├── payload
└── Library
│ ├── LaunchAgents
│ └── com.grahamgilbert.chrome_update_notifier.plist
│ └── Management
│ ├── chrome_update_notifier.py
│ └── show_chrome_update_message.sh
└── scripts
└── postinstall
/.gitignore:
--------------------------------------------------------------------------------
1 | # .DS_Store files!
2 | .DS_Store
3 |
4 | # our build directory
5 | build/
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chrome Update Notifier
2 |
3 | ## Requirements
4 |
5 | * [Yo](https://github.com/sheagcraig/yo) (a customized version with your organization's logo is even cooler)
6 |
7 | This is a script that is run via a Launch Agent periodically that will ensure that the currently running version of Google Chrome is the same as is on disk. If these differ, it will post a notification via Yo to ask the user to relaunch Google Chrome gracefully (preserving their precious tabs!).
8 |
--------------------------------------------------------------------------------
/build-info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | distribution_style
6 |
7 | identifier
8 | com.grahamgilbert.chrome_update_notifier
9 | install_location
10 | /
11 | name
12 | chrome_update_notifier-${version}.pkg
13 | ownership
14 | recommended
15 | postinstall_action
16 | none
17 | suppress_bundle_relocation
18 |
19 | version
20 | 1.0.1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/payload/Library/LaunchAgents/com.grahamgilbert.chrome_update_notifier.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Label
6 | com.grahamgilbert.chrome_update_notifier
7 | Program
8 | /Library/Management/chrome_update_notifier.py
9 | RunAtLoad
10 |
11 | StartInterval
12 | 300
13 |
14 |
15 |
--------------------------------------------------------------------------------
/payload/Library/Management/chrome_update_notifier.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | """
4 | This will notifify the user via Yo that they need to update Chrome if:
5 | * It is installed
6 | * We haven't already told them about this version
7 | """
8 |
9 | import subprocess
10 | import time
11 | import plistlib
12 | import os
13 | import sys
14 | import getpass
15 | import string
16 | from distutils.version import LooseVersion
17 | from Foundation import \
18 | CFPreferencesSetAppValue, \
19 | CFPreferencesAppSynchronize, \
20 | CFPreferencesCopyAppValue, \
21 | NSDate
22 |
23 | BUNDLE_ID = 'com.grahamgilbert.chrome_notifier'
24 |
25 | TESTING = False
26 |
27 |
28 | def run_yo():
29 | """
30 | Like, runs Yo
31 | """
32 | cmd = [
33 | '/Library/Management/show_chrome_update_message.sh'
34 | ]
35 |
36 | subprocess.call(cmd)
37 |
38 |
39 | def set_pref(pref_name, pref_value):
40 | """
41 | Sets a preference
42 | """
43 | # pylint: disable=E0602
44 | CFPreferencesSetAppValue(pref_name, pref_value, BUNDLE_ID)
45 | CFPreferencesAppSynchronize(BUNDLE_ID)
46 |
47 |
48 | def pref(pref_name):
49 | """
50 | Reads a preference, setting a default if we set any
51 | """
52 | default_prefs = {
53 | # 'last_shown' : NSDate.new(),
54 | }
55 |
56 | pref_value = CFPreferencesCopyAppValue(pref_name, BUNDLE_ID)
57 | if pref_value is None:
58 | pref_value = default_prefs.get(pref_name)
59 | # we're using a default value. We'll write it out to
60 | # /Library/Preferences/.plist for admin
61 | # discoverability
62 | set_pref(pref_name, pref_value)
63 | if isinstance(pref_value, NSDate):
64 | # convert NSDate/CFDates to strings
65 | pref_value = str(pref_value)
66 | return pref_value
67 |
68 |
69 | def already_notified(new_version):
70 | """
71 | Have we notified on this version before?
72 | """
73 | last_version = pref('last_notified')
74 | if last_version is None:
75 | # Never notified
76 | return False
77 | if LooseVersion(new_version) > LooseVersion(last_version):
78 | # new version is bigger, hasn't been notified
79 | return False
80 | else:
81 | # New version isn't bigger, we have been notified
82 | return True
83 |
84 |
85 | def set_last_version_notified(version):
86 | """
87 | This is the last version we notified on
88 | """
89 | set_pref('last_notified', version)
90 |
91 |
92 | def chrome_installed():
93 | """
94 | Checks if Google Chrome is installed
95 | """
96 | chrome = '/Applications/Google Chrome.app'
97 | return bool(os.path.exists(chrome))
98 |
99 |
100 | def get_chrome_version():
101 | """
102 | Returns the on disk chrome version
103 | """
104 | chrome_info = plistlib.readPlist('/Applications/Google Chrome.app/Contents/Info.plist')
105 | if 'CFBundleShortVersionString' in chrome_info:
106 | return chrome_info['CFBundleShortVersionString']
107 | else:
108 | return '0'
109 |
110 |
111 | def running_chrome_version():
112 | proc = subprocess.Popen(['/bin/ps', '-axo' 'command='],
113 | shell=False, stdin=subprocess.PIPE,
114 | stdout=subprocess.PIPE,
115 | stderr=subprocess.PIPE)
116 | (output, dummy_err) = proc.communicate()
117 | if proc.returncode == 0:
118 | proc_lines = [item for item in output.splitlines()]
119 | for line in proc_lines:
120 | if line.startswith('/Applications/Google Chrome.app/Contents/Versions/'):
121 | line = line.replace('/Applications/Google Chrome.app/Contents/Versions/', '')
122 | if line:
123 | sep = '/'
124 | line = line.split(sep, 1)[0]
125 | return line
126 |
127 |
128 | def main():
129 | """
130 | gimmie some main
131 | """
132 |
133 | running = False
134 |
135 | running_version = running_chrome_version()
136 | if not running_version:
137 | print 'Chrome is not running'
138 | time.sleep(30)
139 | sys.exit(0)
140 |
141 | if TESTING is True:
142 | running = True
143 |
144 | if TESTING is True:
145 | sleep_seconds = 0
146 | else:
147 | sleep_seconds = 20
148 |
149 | if not chrome_installed():
150 | print 'Chrome not installed'
151 | time.sleep(30)
152 | sys.exit(0)
153 |
154 | current_chrome_version = get_chrome_version()
155 |
156 | if current_chrome_version == '0':
157 | print 'Could not get current chrome version'
158 | time.sleep(30)
159 | sys.exit(0)
160 |
161 | if LooseVersion(running_version) < LooseVersion(current_chrome_version):
162 | print "running version is {}".format(running_version)
163 | print "on disk version is {}".format(current_chrome_version)
164 | running = True
165 |
166 | if already_notified(get_chrome_version()):
167 | print 'Already notified for this version'
168 | time.sleep(30)
169 | sys.exit(0)
170 |
171 | if running is True or TESTING is True:
172 | print 'We are running...'
173 | run_yo()
174 | time.sleep(sleep_seconds)
175 | set_last_version_notified(get_chrome_version())
176 |
177 |
178 | if __name__ == '__main__':
179 | main()
180 |
--------------------------------------------------------------------------------
/payload/Library/Management/show_chrome_update_message.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | /usr/local/bin/yo_scheduler --title "Google Chrome Update" --info "Google Chrome needs to be restarted so it can be updated. Tabs will be restored." -B "osascript -e 'tell application \"Google Chrome\" to open location \"chrome://restart\"'" --action-btn "Restart"
4 |
--------------------------------------------------------------------------------
/scripts/postinstall:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # get console UID
4 | consoleuser=`/usr/bin/stat -f "%Su" /dev/console | /usr/bin/xargs /usr/bin/id -u`
5 |
6 | /bin/launchctl bootout gui/$consoleuser /Library/LaunchAgents/com.grahamgilbert.chrome_update_notifier.plist
7 | /bin/launchctl bootstrap gui/$consoleuser /Library/LaunchAgents/com.grahamgilbert.chrome_update_notifier.plist
8 |
9 | exit 0
10 |
--------------------------------------------------------------------------------