|
1 #!/usr/bin/env zsh |
|
2 [ "${ZSH_VERSION:-}" = "" ] && echo >&2 "Only works with zsh" && exit 1 |
|
3 setopt err_exit no_unset pipefail extended_glob |
|
4 |
|
5 # Simple script to update the godoc comments on all watchers. Probably took me |
|
6 # more time to write this than doing it manually, but ah well 🙃 |
|
7 |
|
8 watcher=$(<<EOF |
|
9 // Watcher watches a set of paths, delivering events on a channel. |
|
10 // |
|
11 // A watcher should not be copied (e.g. pass it by pointer, rather than by |
|
12 // value). |
|
13 // |
|
14 // # Linux notes |
|
15 // |
|
16 // When a file is removed a Remove event won't be emitted until all file |
|
17 // descriptors are closed, and deletes will always emit a Chmod. For example: |
|
18 // |
|
19 // fp := os.Open("file") |
|
20 // os.Remove("file") // Triggers Chmod |
|
21 // fp.Close() // Triggers Remove |
|
22 // |
|
23 // This is the event that inotify sends, so not much can be changed about this. |
|
24 // |
|
25 // The fs.inotify.max_user_watches sysctl variable specifies the upper limit |
|
26 // for the number of watches per user, and fs.inotify.max_user_instances |
|
27 // specifies the maximum number of inotify instances per user. Every Watcher you |
|
28 // create is an "instance", and every path you add is a "watch". |
|
29 // |
|
30 // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and |
|
31 // /proc/sys/fs/inotify/max_user_instances |
|
32 // |
|
33 // To increase them you can use sysctl or write the value to the /proc file: |
|
34 // |
|
35 // # Default values on Linux 5.18 |
|
36 // sysctl fs.inotify.max_user_watches=124983 |
|
37 // sysctl fs.inotify.max_user_instances=128 |
|
38 // |
|
39 // To make the changes persist on reboot edit /etc/sysctl.conf or |
|
40 // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check |
|
41 // your distro's documentation): |
|
42 // |
|
43 // fs.inotify.max_user_watches=124983 |
|
44 // fs.inotify.max_user_instances=128 |
|
45 // |
|
46 // Reaching the limit will result in a "no space left on device" or "too many open |
|
47 // files" error. |
|
48 // |
|
49 // # kqueue notes (macOS, BSD) |
|
50 // |
|
51 // kqueue requires opening a file descriptor for every file that's being watched; |
|
52 // so if you're watching a directory with five files then that's six file |
|
53 // descriptors. You will run in to your system's "max open files" limit faster on |
|
54 // these platforms. |
|
55 // |
|
56 // The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to |
|
57 // control the maximum number of open files, as well as /etc/login.conf on BSD |
|
58 // systems. |
|
59 // |
|
60 // # macOS notes |
|
61 // |
|
62 // Spotlight indexing on macOS can result in multiple events (see [#15]). A |
|
63 // temporary workaround is to add your folder(s) to the "Spotlight Privacy |
|
64 // Settings" until we have a native FSEvents implementation (see [#11]). |
|
65 // |
|
66 // [#11]: https://github.com/fsnotify/fsnotify/issues/11 |
|
67 // [#15]: https://github.com/fsnotify/fsnotify/issues/15 |
|
68 EOF |
|
69 ) |
|
70 |
|
71 new=$(<<EOF |
|
72 // NewWatcher creates a new Watcher. |
|
73 EOF |
|
74 ) |
|
75 |
|
76 add=$(<<EOF |
|
77 // Add starts monitoring the path for changes. |
|
78 // |
|
79 // A path can only be watched once; attempting to watch it more than once will |
|
80 // return an error. Paths that do not yet exist on the filesystem cannot be |
|
81 // added. A watch will be automatically removed if the path is deleted. |
|
82 // |
|
83 // A path will remain watched if it gets renamed to somewhere else on the same |
|
84 // filesystem, but the monitor will get removed if the path gets deleted and |
|
85 // re-created, or if it's moved to a different filesystem. |
|
86 // |
|
87 // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special |
|
88 // filesystems (/proc, /sys, etc.) generally don't work. |
|
89 // |
|
90 // # Watching directories |
|
91 // |
|
92 // All files in a directory are monitored, including new files that are created |
|
93 // after the watcher is started. Subdirectories are not watched (i.e. it's |
|
94 // non-recursive). |
|
95 // |
|
96 // # Watching files |
|
97 // |
|
98 // Watching individual files (rather than directories) is generally not |
|
99 // recommended as many tools update files atomically. Instead of "just" writing |
|
100 // to the file a temporary file will be written to first, and if successful the |
|
101 // temporary file is moved to to destination removing the original, or some |
|
102 // variant thereof. The watcher on the original file is now lost, as it no |
|
103 // longer exists. |
|
104 // |
|
105 // Instead, watch the parent directory and use Event.Name to filter out files |
|
106 // you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. |
|
107 EOF |
|
108 ) |
|
109 |
|
110 remove=$(<<EOF |
|
111 // Remove stops monitoring the path for changes. |
|
112 // |
|
113 // Directories are always removed non-recursively. For example, if you added |
|
114 // /tmp/dir and /tmp/dir/subdir then you will need to remove both. |
|
115 // |
|
116 // Removing a path that has not yet been added returns [ErrNonExistentWatch]. |
|
117 EOF |
|
118 ) |
|
119 |
|
120 close=$(<<EOF |
|
121 // Close removes all watches and closes the events channel. |
|
122 EOF |
|
123 ) |
|
124 |
|
125 watchlist=$(<<EOF |
|
126 // WatchList returns all paths added with [Add] (and are not yet removed). |
|
127 EOF |
|
128 ) |
|
129 |
|
130 events=$(<<EOF |
|
131 // Events sends the filesystem change events. |
|
132 // |
|
133 // fsnotify can send the following events; a "path" here can refer to a |
|
134 // file, directory, symbolic link, or special file like a FIFO. |
|
135 // |
|
136 // fsnotify.Create A new path was created; this may be followed by one |
|
137 // or more Write events if data also gets written to a |
|
138 // file. |
|
139 // |
|
140 // fsnotify.Remove A path was removed. |
|
141 // |
|
142 // fsnotify.Rename A path was renamed. A rename is always sent with the |
|
143 // old path as Event.Name, and a Create event will be |
|
144 // sent with the new name. Renames are only sent for |
|
145 // paths that are currently watched; e.g. moving an |
|
146 // unmonitored file into a monitored directory will |
|
147 // show up as just a Create. Similarly, renaming a file |
|
148 // to outside a monitored directory will show up as |
|
149 // only a Rename. |
|
150 // |
|
151 // fsnotify.Write A file or named pipe was written to. A Truncate will |
|
152 // also trigger a Write. A single "write action" |
|
153 // initiated by the user may show up as one or multiple |
|
154 // writes, depending on when the system syncs things to |
|
155 // disk. For example when compiling a large Go program |
|
156 // you may get hundreds of Write events, so you |
|
157 // probably want to wait until you've stopped receiving |
|
158 // them (see the dedup example in cmd/fsnotify). |
|
159 // |
|
160 // fsnotify.Chmod Attributes were changed. On Linux this is also sent |
|
161 // when a file is removed (or more accurately, when a |
|
162 // link to an inode is removed). On kqueue it's sent |
|
163 // and on kqueue when a file is truncated. On Windows |
|
164 // it's never sent. |
|
165 EOF |
|
166 ) |
|
167 |
|
168 errors=$(<<EOF |
|
169 // Errors sends any errors. |
|
170 EOF |
|
171 ) |
|
172 |
|
173 set-cmt() { |
|
174 local pat=$1 |
|
175 local cmt=$2 |
|
176 |
|
177 IFS=$'\n' local files=($(grep -n $pat backend_*~*_test.go)) |
|
178 for f in $files; do |
|
179 IFS=':' local fields=($=f) |
|
180 local file=$fields[1] |
|
181 local end=$(( $fields[2] - 1 )) |
|
182 |
|
183 # Find start of comment. |
|
184 local start=0 |
|
185 IFS=$'\n' local lines=($(head -n$end $file)) |
|
186 for (( i = 1; i <= $#lines; i++ )); do |
|
187 local line=$lines[-$i] |
|
188 if ! grep -q '^[[:space:]]*//' <<<$line; then |
|
189 start=$(( end - (i - 2) )) |
|
190 break |
|
191 fi |
|
192 done |
|
193 |
|
194 head -n $(( start - 1 )) $file >/tmp/x |
|
195 print -r -- $cmt >>/tmp/x |
|
196 tail -n+$(( end + 1 )) $file >>/tmp/x |
|
197 mv /tmp/x $file |
|
198 done |
|
199 } |
|
200 |
|
201 set-cmt '^type Watcher struct ' $watcher |
|
202 set-cmt '^func NewWatcher(' $new |
|
203 set-cmt '^func (w \*Watcher) Add(' $add |
|
204 set-cmt '^func (w \*Watcher) Remove(' $remove |
|
205 set-cmt '^func (w \*Watcher) Close(' $close |
|
206 set-cmt '^func (w \*Watcher) WatchList(' $watchlist |
|
207 set-cmt '^[[:space:]]*Events *chan Event$' $events |
|
208 set-cmt '^[[:space:]]*Errors *chan error$' $errors |