frugal-iot-server 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/dynsec-server.js +34 -1
- package/package.json +3 -3
- package/public/service-worker.js +1 -1
- package/scripts/check-schema.js +24 -4
- package/scripts/copy-schema-to-examples.zsh +18 -3
- package/scripts/prerelease.zsh +84 -33
package/lib/dynsec-server.js
CHANGED
|
@@ -34,6 +34,26 @@ function dynsecConfigured(config) {
|
|
|
34
34
|
return !!(s.dynsec_admin_user && s.dynsec_admin_password);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/*
|
|
38
|
+
* Another server owns this broker: derive credentials, never try to create them.
|
|
39
|
+
*
|
|
40
|
+
* For working on the server or the client against live data - a laptop pointed at the production
|
|
41
|
+
* broker. It has no admin credential and must not have one, but the accounts it needs already
|
|
42
|
+
* exist, created by the server that does. Given the same user_secret it derives exactly the same
|
|
43
|
+
* passwords, so what it hands a browser works.
|
|
44
|
+
*
|
|
45
|
+
* Declared in config.d/secrets.yaml rather than config.d/mqtt.yaml, for two reasons: that file is
|
|
46
|
+
* never committed, so this cannot be switched on by accident in a checkout; and it is where the
|
|
47
|
+
* borrowed user_secret has to go anyway, which is the real access control here. Without that
|
|
48
|
+
* secret a checkout of this repository cannot reach live data at all, whatever it sets.
|
|
49
|
+
*
|
|
50
|
+
* What does NOT work on such a server, by design: enrolling a node, resetting one, and
|
|
51
|
+
* frugal-iot-rebuild-dynsec - all of which create or delete accounts.
|
|
52
|
+
*/
|
|
53
|
+
function brokerManagedElsewhere(config) {
|
|
54
|
+
return !!(config && config.secrets && config.secrets.broker_managed_elsewhere);
|
|
55
|
+
}
|
|
56
|
+
|
|
37
57
|
// A connection per operation rather than one held open. These happen at login and at permission
|
|
38
58
|
// changes - rare, and never in a hot path - and a short-lived connection cannot go stale, which on
|
|
39
59
|
// a Pi that sleeps or loses its network it otherwise would.
|
|
@@ -69,6 +89,15 @@ export function syncUser(db, config, user, cb) {
|
|
|
69
89
|
reportUnavailable(`user ${user.username}`, err);
|
|
70
90
|
return cb(err);
|
|
71
91
|
}
|
|
92
|
+
// Borrowing another server's broker: the account is already there, so derive and hand it over
|
|
93
|
+
// without trying to reach a plugin this server has no credential for.
|
|
94
|
+
if (brokerManagedElsewhere(config)) {
|
|
95
|
+
return cb(null, {
|
|
96
|
+
username: names.userClient(user.username),
|
|
97
|
+
password: derivePassword(userSecret, user.username, user.hashed_password),
|
|
98
|
+
brokerUpdated: false,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
72
101
|
// Nothing to hand a browser on a server that has never driven the plugin: the account cannot
|
|
73
102
|
// exist, so the dashboard says live data is unavailable rather than failing to connect with a
|
|
74
103
|
// credential that was never real. See dynsecConfigured.
|
|
@@ -136,7 +165,7 @@ export function syncLoggers(config, orgs, cb) {
|
|
|
136
165
|
const creds = {};
|
|
137
166
|
// No credentials at all means the logger falls back to the organization's shared password, which
|
|
138
167
|
// works until S8. That is the right answer whenever an account of its own could not exist.
|
|
139
|
-
if (!userSecret || !dynsecConfigured(config)) {
|
|
168
|
+
if (!userSecret || (!dynsecConfigured(config) && !brokerManagedElsewhere(config))) {
|
|
140
169
|
reportUnavailable('logger accounts',
|
|
141
170
|
new Error(userSecret ? 'No dynsec_admin_user in config.d/secrets.yaml'
|
|
142
171
|
: 'No user_secret in config.d/secrets.yaml'));
|
|
@@ -145,6 +174,10 @@ export function syncLoggers(config, orgs, cb) {
|
|
|
145
174
|
for (const org of orgs) {
|
|
146
175
|
creds[org] = { username: names.loggerClient(org), password: deriveLoggerPassword(userSecret, org) };
|
|
147
176
|
}
|
|
177
|
+
// Same as for a user: the accounts exist, this server just did not make them. Two loggers on one
|
|
178
|
+
// account is fine - mqtt.js generates a client id per connection, and MQTT evicts by client id,
|
|
179
|
+
// not by user - so a laptop recording alongside production does not disturb it.
|
|
180
|
+
if (brokerManagedElsewhere(config)) return cb(null, creds);
|
|
148
181
|
withDynsec(config, (cerr, dynsec, done) => {
|
|
149
182
|
// Configured but not answering: the accounts are almost certainly there from an earlier run,
|
|
150
183
|
// so the derived credentials are handed over and the logger reconnects when the broker returns.
|
package/package.json
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"cookie-parser": "^1.4.7",
|
|
13
13
|
"express": "^5.2.1",
|
|
14
14
|
"express-session": "^1.19.0",
|
|
15
|
-
"frugal-iot-client": "^2.0.
|
|
16
|
-
"frugal-iot-logger": "^2.0.
|
|
15
|
+
"frugal-iot-client": "^2.0.1",
|
|
16
|
+
"frugal-iot-logger": "^2.0.1",
|
|
17
17
|
"hash-wasm": "^4.12.0",
|
|
18
18
|
"js-yaml": "^4.2.0",
|
|
19
19
|
"morgan": "^1.11.0",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"check-files": "node scripts/check-published-files.js"
|
|
84
84
|
},
|
|
85
85
|
"type": "module",
|
|
86
|
-
"version": "2.0.
|
|
86
|
+
"version": "2.0.1"
|
|
87
87
|
}
|
package/public/service-worker.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// it, an installed browser keeps serving the files it already cached - and since the list now covers
|
|
7
7
|
// the dashboard's own code, that means old JS behind freshly fetched HTML. Any edit to the list
|
|
8
8
|
// needs this bumped, or it does not reach anyone who already has the old one.
|
|
9
|
-
const CACHE_NAME = 'frugal-iot-cache-2.0.
|
|
9
|
+
const CACHE_NAME = 'frugal-iot-cache-2.0.1';
|
|
10
10
|
/*
|
|
11
11
|
* Everything the app needs, so that a phone on a poor or expensive link fetches it once.
|
|
12
12
|
*
|
package/scripts/check-schema.js
CHANGED
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
* is what decides how many decimals a reading is shown to; without it the UI guesses.
|
|
26
26
|
*
|
|
27
27
|
* - A numeric topic with no "units". Often right - plenty of readings are dimensionless - but
|
|
28
|
-
* worth a look, because the UI has nothing to put after the number.
|
|
28
|
+
* worth a look, because the UI has nothing to put after the number. The topics that are
|
|
29
|
+
* deliberately dimensionless are listed in UNITS_EXEMPT below, and are not mentioned.
|
|
29
30
|
*
|
|
30
31
|
* And, separately, things that are never deliberate and so count as errors:
|
|
31
32
|
*
|
|
@@ -35,8 +36,13 @@
|
|
|
35
36
|
* Usage:
|
|
36
37
|
* node scripts/check-schema.js # checks ./config.d/schema
|
|
37
38
|
* node scripts/check-schema.js <dir> [<dir> ...] # each dir holding topics.yaml and modules.yaml
|
|
39
|
+
* node scripts/check-schema.js -q <dir> # say nothing unless there is something to say
|
|
38
40
|
* node scripts/check-schema.js --resolve <otakey> # which devices.yaml entry that OTA key uses
|
|
39
41
|
*
|
|
42
|
+
* -q (--quiet) prints nothing at all when a schema is clean, so it can run from a release script
|
|
43
|
+
* without burying the one line that matters. Warnings and errors print exactly as they otherwise
|
|
44
|
+
* would, and the exit code is the same either way.
|
|
45
|
+
*
|
|
40
46
|
* Exits 0 for warnings, so it can run from a prerelease script without blocking a release for
|
|
41
47
|
* something that may well be deliberate. Exits 1 for an error, or if it could not read a schema.
|
|
42
48
|
* Both callers guard it with "|| true", so neither blocks on either.
|
|
@@ -51,6 +57,17 @@ import path from 'path';
|
|
|
51
57
|
const TYPES_LOGGED_BY_DEFAULT = ['float', 'int', 'bool'];
|
|
52
58
|
// The types with decimals to decide, and so the ones that need a width
|
|
53
59
|
const NUMERIC_TYPES = ['float', 'exponential'];
|
|
60
|
+
/*
|
|
61
|
+
* Topics that are dimensionless on purpose, so "no units" is the right answer rather than
|
|
62
|
+
* something to look at. Without this the same three warnings appear at every release, which is
|
|
63
|
+
* exactly how a real one comes to be skimmed past. Add a name here only once you are sure the
|
|
64
|
+
* reading genuinely has no unit - not because you have not decided yet.
|
|
65
|
+
*/
|
|
66
|
+
const UNITS_EXEMPT = [
|
|
67
|
+
'controlfloat', // a control's value, in whatever the thing it controls is measured in
|
|
68
|
+
'hdop', // dilution of precision - a ratio
|
|
69
|
+
'loadcell', // raw ADC counts until a calibration turns them into grams
|
|
70
|
+
];
|
|
54
71
|
|
|
55
72
|
function loadYaml(file) {
|
|
56
73
|
if (!existsSync(file)) return null;
|
|
@@ -111,7 +128,7 @@ function checkSchemaDir(dir) {
|
|
|
111
128
|
+ ` characters before any decimal point, so values will overflow it`);
|
|
112
129
|
}
|
|
113
130
|
}
|
|
114
|
-
if (hasDecimals && (topic.units === undefined)) {
|
|
131
|
+
if (hasDecimals && (topic.units === undefined) && !UNITS_EXEMPT.includes(name)) {
|
|
115
132
|
warnings.push(`topic "${name}" has no "units" - fine if it is dimensionless, but the UI has`
|
|
116
133
|
+ ` nothing to put after the number`);
|
|
117
134
|
}
|
|
@@ -187,6 +204,7 @@ function resolveDeviceEntry(devices, otakey) {
|
|
|
187
204
|
}
|
|
188
205
|
|
|
189
206
|
const argv = process.argv.slice(2);
|
|
207
|
+
const quiet = argv.includes('-q') || argv.includes('--quiet');
|
|
190
208
|
const resolveAt = argv.indexOf('--resolve');
|
|
191
209
|
if (resolveAt !== -1) {
|
|
192
210
|
const otakey = argv[resolveAt + 1];
|
|
@@ -201,7 +219,8 @@ if (resolveAt !== -1) {
|
|
|
201
219
|
process.exit(0);
|
|
202
220
|
}
|
|
203
221
|
|
|
204
|
-
const
|
|
222
|
+
const named = argv.filter((a) => !a.startsWith('-'));
|
|
223
|
+
const dirs = named.length ? named : ['config.d/schema'];
|
|
205
224
|
let unreadable = 0;
|
|
206
225
|
let totalWarnings = 0;
|
|
207
226
|
let totalErrors = 0;
|
|
@@ -214,7 +233,8 @@ for (const dir of dirs) {
|
|
|
214
233
|
continue;
|
|
215
234
|
}
|
|
216
235
|
if (!warnings.length && !errors.length) {
|
|
217
|
-
|
|
236
|
+
// Under -q a clean schema says nothing, so whatever a release script does print is a finding
|
|
237
|
+
if (!quiet) console.log(`${dir}: nothing to report`);
|
|
218
238
|
continue;
|
|
219
239
|
}
|
|
220
240
|
if (warnings.length) {
|
|
@@ -8,16 +8,31 @@
|
|
|
8
8
|
# schema is maintained.
|
|
9
9
|
#
|
|
10
10
|
# Usage:
|
|
11
|
-
# scripts/copy-schema-to-examples.zsh [<frugal-iot-logger directory>]
|
|
11
|
+
# scripts/copy-schema-to-examples.zsh [-q|--quiet] [<frugal-iot-logger directory>]
|
|
12
12
|
#
|
|
13
13
|
# With no argument it looks for frugal-iot-logger beside this checkout, which is how these are
|
|
14
14
|
# developed. Says what it did and changes nothing else; committing is up to you.
|
|
15
|
+
#
|
|
16
|
+
# -q (--quiet) names only the files it changed. A run that copies nothing says nothing, so from a
|
|
17
|
+
# release script the output is the list of files you now have to commit.
|
|
15
18
|
|
|
16
19
|
set -euo pipefail
|
|
17
20
|
|
|
21
|
+
QUIET=0
|
|
22
|
+
ARGS=()
|
|
23
|
+
for arg in "$@"; do
|
|
24
|
+
case "$arg" in
|
|
25
|
+
-q|--quiet) QUIET=1 ;;
|
|
26
|
+
*) ARGS+=("$arg") ;;
|
|
27
|
+
esac
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
# Progress, as opposed to a change or a problem: silent under -q
|
|
31
|
+
say() { (( QUIET )) || print -r -- "$@" }
|
|
32
|
+
|
|
18
33
|
HERE="${0:A:h:h}" # The frugal-iot-server checkout this script is in
|
|
19
34
|
SCHEMA="${HERE}/config.d/schema"
|
|
20
|
-
LOGGER="${1:-${HERE:h}/frugal-iot-logger}"
|
|
35
|
+
LOGGER="${ARGS[1]:-${HERE:h}/frugal-iot-logger}"
|
|
21
36
|
|
|
22
37
|
if [[ ! -d "$LOGGER" ]]; then
|
|
23
38
|
echo " No frugal-iot-logger checkout at ${LOGGER}, so nothing copied."
|
|
@@ -44,7 +59,7 @@ COPIED=0
|
|
|
44
59
|
for d in $DIRS; do
|
|
45
60
|
for f in $FILES; do
|
|
46
61
|
if cmp -s "${SCHEMA}/${f}" "${d}/${f}"; then
|
|
47
|
-
|
|
62
|
+
say " same ${d#${LOGGER}/}/${f}"
|
|
48
63
|
else
|
|
49
64
|
cp "${SCHEMA}/${f}" "${d}/${f}"
|
|
50
65
|
echo " updated ${d#${LOGGER}/}/${f}"
|
package/scripts/prerelease.zsh
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
# afterwards. Run it, read what it says, then publish:
|
|
5
5
|
#
|
|
6
6
|
# npm run prerelease
|
|
7
|
+
# zsh scripts/prerelease.zsh --release-version 2.1.0
|
|
8
|
+
# zsh scripts/prerelease.zsh -q
|
|
9
|
+
#
|
|
10
|
+
# -q (--quiet) prints only findings and changes, so a clean run prints nothing at all. That is what
|
|
11
|
+
# the release script uses; run it without -q to see each check confirm itself.
|
|
12
|
+
#
|
|
13
|
+
# --release-version says which version this release is about to publish everywhere, and is what the
|
|
14
|
+
# release script in frugal-iot-demo passes. Without it, "requires a version nobody has published"
|
|
15
|
+
# is a blocker, because that is what it means on an ordinary day. During a release it is the normal
|
|
16
|
+
# state of affairs - the logger and client are about to be published at that version and have not
|
|
17
|
+
# been yet - so with it, a range that only the version being released satisfies is reported and
|
|
18
|
+
# allowed, and CACHE_NAME is set to that version rather than to whatever is on npm today.
|
|
7
19
|
#
|
|
8
20
|
# It checks the sensor schema and copies it into the examples that ship with frugal-iot-logger, sets
|
|
9
21
|
# the service worker's cache version, and looks for the package being wired to a checkout on this
|
|
@@ -21,27 +33,52 @@
|
|
|
21
33
|
|
|
22
34
|
set -euo pipefail
|
|
23
35
|
|
|
36
|
+
RELEASE_VERSION=""
|
|
37
|
+
QUIET=0
|
|
38
|
+
while (( $# )); do
|
|
39
|
+
case "$1" in
|
|
40
|
+
--release-version) RELEASE_VERSION="${2:-}"; shift 2 || true ;;
|
|
41
|
+
--release-version=*) RELEASE_VERSION="${1#*=}"; shift ;;
|
|
42
|
+
-q|--quiet) QUIET=1; shift ;;
|
|
43
|
+
-h|--help) sed -n '2,34p' "$0" | sed 's/^# \?//'; exit 0 ;;
|
|
44
|
+
*) print -u2 "Unknown option: $1 (try --release-version <version>, -q, or -h)"; exit 2 ;;
|
|
45
|
+
esac
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
# A check confirming itself, as opposed to a finding or a change: silent under -q
|
|
49
|
+
say() { (( QUIET )) || print -r -- "$@" }
|
|
50
|
+
|
|
51
|
+
# Passed on to the scripts that take the same flag
|
|
52
|
+
QARG=()
|
|
53
|
+
(( QUIET )) && QARG=(-q)
|
|
54
|
+
|
|
24
55
|
HERE="${0:A:h:h}" # The frugal-iot-server checkout this script is in
|
|
25
56
|
SCHEMA="${HERE}/config.d/schema"
|
|
26
57
|
|
|
58
|
+
# Does the version being released satisfy this range? Only asked when --release-version was given.
|
|
59
|
+
satisfied_by_release() {
|
|
60
|
+
[[ -n "$RELEASE_VERSION" ]] || return 1
|
|
61
|
+
node -e "process.exit(require('semver').satisfies('${RELEASE_VERSION}', process.argv[1]) ? 0 : 1)" "$1" 2>/dev/null
|
|
62
|
+
}
|
|
63
|
+
|
|
27
64
|
cd "$HERE"
|
|
28
65
|
|
|
29
66
|
BLOCKERS=() # never right in a published package - this script exits non-zero for these
|
|
30
67
|
WARNINGS=() # probably worth doing something about, but yours to judge
|
|
31
68
|
|
|
32
|
-
|
|
33
|
-
node scripts/check-schema.js config.d/schema || true
|
|
69
|
+
say "=== Checking ${SCHEMA} ==="
|
|
70
|
+
node scripts/check-schema.js "${QARG[@]}" config.d/schema || true
|
|
34
71
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
zsh scripts/copy-schema-to-examples.zsh
|
|
72
|
+
say
|
|
73
|
+
say "=== Copying the schema into the frugal-iot-logger examples ==="
|
|
74
|
+
zsh scripts/copy-schema-to-examples.zsh "${QARG[@]}"
|
|
38
75
|
|
|
39
76
|
# ---- Is this package wired to a checkout on this machine? ----
|
|
40
77
|
# Developing the server, logger and client together means pointing the server at sibling checkouts,
|
|
41
78
|
# and every way of doing that is invisible once you have got used to it. Published, each one either
|
|
42
79
|
# breaks every install outright or ships code nobody else has.
|
|
43
|
-
|
|
44
|
-
|
|
80
|
+
say
|
|
81
|
+
say "=== Checking nothing is wired to a local checkout ==="
|
|
45
82
|
|
|
46
83
|
# 1. "file:" or "link:" dependencies - these would make "npm install frugal-iot-server" fail for
|
|
47
84
|
# everybody, because the path they name does not exist on anyone else's machine
|
|
@@ -59,7 +96,7 @@ if [[ -n "$LOCALDEPS" ]]; then
|
|
|
59
96
|
print -r -- "$LOCALDEPS" | sed 's/^/ /'
|
|
60
97
|
BLOCKERS+=("package.json names a dependency by path - publishing that breaks every install. Put the version range back.")
|
|
61
98
|
else
|
|
62
|
-
|
|
99
|
+
say " package.json names no dependencies by path"
|
|
63
100
|
fi
|
|
64
101
|
|
|
65
102
|
# 2. npm link - node_modules/<pkg> being a symlink means what you have been testing is your
|
|
@@ -74,8 +111,9 @@ if (( ${#LINKED} )); then
|
|
|
74
111
|
for l in $LINKED; do echo " linked: $l"; done
|
|
75
112
|
echo " What you have been running is that checkout, not the published package."
|
|
76
113
|
echo " Publish the linked package first, then require the new version here."
|
|
114
|
+
WARNINGS+=("node_modules links to a checkout (${(j:, :)LINKED}) - what the tests exercised is that checkout, not the published package.")
|
|
77
115
|
else
|
|
78
|
-
|
|
116
|
+
say " node_modules holds published packages, not links to checkouts"
|
|
79
117
|
fi
|
|
80
118
|
|
|
81
119
|
# 3. The development alternatives inside the source, which are switched by editing rather than by
|
|
@@ -83,12 +121,12 @@ fi
|
|
|
83
121
|
if grep -qE '^\s*import .*from\s+"\.\./frugal-iot-logger' frugal-iot-server.js 2>/dev/null; then
|
|
84
122
|
BLOCKERS+=("frugal-iot-server.js is importing the logger from ../frugal-iot-logger - comment that line out and uncomment the \"frugal-iot-logger\" one.")
|
|
85
123
|
else
|
|
86
|
-
|
|
124
|
+
say " frugal-iot-server.js imports the logger as a package, not by path"
|
|
87
125
|
fi
|
|
88
126
|
if grep -qE '^\s*(htmldir|nodemodulesdir):\s*\.\./' config.d/server.yaml 2>/dev/null; then
|
|
89
127
|
BLOCKERS+=("config.d/server.yaml points htmldir or nodemodulesdir at ../frugal-iot-client - that is the development setting and ships to everyone who installs.")
|
|
90
128
|
else
|
|
91
|
-
|
|
129
|
+
say " config.d/server.yaml points at ./node_modules, not at a sibling checkout"
|
|
92
130
|
fi
|
|
93
131
|
|
|
94
132
|
# ---- Are the versions this requires actually available? ----
|
|
@@ -96,8 +134,8 @@ fi
|
|
|
96
134
|
# on a Pi as well as here, and nothing in the repo shows it. Asking for one that is published but
|
|
97
135
|
# not the newest is a lesser problem - a fresh install resolves to the newest anyway, but an
|
|
98
136
|
# existing one is free to stay where it is, so your changes reach nobody who already has it.
|
|
99
|
-
|
|
100
|
-
|
|
137
|
+
say
|
|
138
|
+
say "=== Checking the versions this requires have been published ==="
|
|
101
139
|
|
|
102
140
|
# Highest published version of $1 satisfying range $2, or empty if none - and it prints nothing and
|
|
103
141
|
# exits 1 when nothing matches, which is an answer rather than a failure, hence the "|| true"
|
|
@@ -110,15 +148,19 @@ for p in frugal-iot-logger frugal-iot-client; do
|
|
|
110
148
|
RANGE=$(node -e "console.log(require('./package.json').dependencies['$p'] || '')")
|
|
111
149
|
if [[ -z "$RANGE" ]]; then continue; fi
|
|
112
150
|
if ! LATEST=$(npm view "$p" version 2>/dev/null); then
|
|
113
|
-
|
|
151
|
+
say " $p: could not ask npm (offline?) - requires ${RANGE}, check it yourself"
|
|
114
152
|
continue
|
|
115
153
|
fi
|
|
116
154
|
MATCH=$(published_match "$p" "$RANGE")
|
|
117
|
-
if [[ -z "$MATCH" ]]; then
|
|
118
|
-
|
|
155
|
+
if [[ -z "$MATCH" ]] && satisfied_by_release "$RANGE"; then
|
|
156
|
+
# Mid-release: this range was raised to the version being published, and $p has not been
|
|
157
|
+
# published at it yet. Expected, and the publishing step is about to deal with it.
|
|
158
|
+
say " $p: requires ${RANGE}, awaiting the ${RELEASE_VERSION} publish (newest so far is ${LATEST})"
|
|
159
|
+
elif [[ -z "$MATCH" ]]; then
|
|
160
|
+
say " $p: requires ${RANGE}, but the newest published is ${LATEST}"
|
|
119
161
|
BLOCKERS+=("Nothing published satisfies ${p}@${RANGE} (newest is ${LATEST}) - publish ${p} first, or every \"npm install frugal-iot-server\" fails.")
|
|
120
162
|
elif [[ "$MATCH" != "$LATEST" ]]; then
|
|
121
|
-
|
|
163
|
+
say " $p: requires ${RANGE} -> ${MATCH}, but ${LATEST} is published"
|
|
122
164
|
WARNINGS+=("${p}@${RANGE} resolves to ${MATCH}, not the newest published ${LATEST} - bump the range if you meant installs to pick up ${LATEST}.")
|
|
123
165
|
else
|
|
124
166
|
# The range allows the newest, but if its floor is older then an existing install can sit on
|
|
@@ -126,10 +168,10 @@ for p in frugal-iot-logger frugal-iot-client; do
|
|
|
126
168
|
FLOOR=${RANGE#[\^~>=]}
|
|
127
169
|
FLOOR=${FLOOR#=}
|
|
128
170
|
if [[ -n "$FLOOR" && "$FLOOR" != "$LATEST" ]]; then
|
|
129
|
-
|
|
171
|
+
say " $p: requires ${RANGE}, newest published ${LATEST} (an existing install may stay on ${FLOOR})"
|
|
130
172
|
WARNINGS+=("${p} requires ${RANGE}, so an install that already has ${FLOOR} is not obliged to move to ${LATEST}. Raise the range to ^${LATEST} if this release needs it.")
|
|
131
173
|
else
|
|
132
|
-
|
|
174
|
+
say " $p: requires ${RANGE}, newest published ${LATEST} - in step"
|
|
133
175
|
fi
|
|
134
176
|
fi
|
|
135
177
|
done
|
|
@@ -139,27 +181,33 @@ done
|
|
|
139
181
|
# installed PWA fetch it again. Left behind, people keep running the client they already had, which
|
|
140
182
|
# looks exactly like a release that did nothing. It tracks the client version, so set it from
|
|
141
183
|
# whichever published client this release will actually resolve to.
|
|
142
|
-
|
|
143
|
-
|
|
184
|
+
say
|
|
185
|
+
say "=== Checking the service worker's cache version ==="
|
|
144
186
|
SW=public/service-worker.js
|
|
145
187
|
CLIENT_RANGE=$(node -e "console.log(require('./package.json').dependencies['frugal-iot-client'] || '')")
|
|
146
188
|
if [[ ! -f "$SW" ]]; then
|
|
147
|
-
|
|
189
|
+
say " no ${SW} here"
|
|
148
190
|
elif [[ -z "$CLIENT_RANGE" ]]; then
|
|
149
|
-
|
|
191
|
+
say " package.json has no frugal-iot-client dependency to match against"
|
|
150
192
|
else
|
|
151
193
|
SW_VERSION=$(sed -n "s/^const CACHE_NAME = 'frugal-iot-cache-\([^']*\)'.*/\1/p" "$SW" | head -1)
|
|
152
194
|
CLIENT_LATEST=$(npm view frugal-iot-client version 2>/dev/null || true)
|
|
153
195
|
CLIENT_WANTED=$(published_match frugal-iot-client "$CLIENT_RANGE")
|
|
196
|
+
# Mid-release the client this ships with is the one about to be published, not the newest on npm
|
|
197
|
+
# today, so follow that instead - otherwise CACHE_NAME would be set back to last month's version.
|
|
198
|
+
if satisfied_by_release "$CLIENT_RANGE"; then
|
|
199
|
+
CLIENT_WANTED="$RELEASE_VERSION"
|
|
200
|
+
CLIENT_LATEST="$RELEASE_VERSION"
|
|
201
|
+
fi
|
|
154
202
|
if [[ -z "$SW_VERSION" ]]; then
|
|
155
|
-
|
|
203
|
+
say " could not find a CACHE_NAME line of the form frugal-iot-cache-<version> in ${SW}"
|
|
156
204
|
WARNINGS+=("Could not read CACHE_NAME from ${SW} - check by hand that it changes when the client does.")
|
|
157
205
|
elif [[ -z "$CLIENT_WANTED" ]]; then
|
|
158
|
-
|
|
206
|
+
say " CACHE_NAME says ${SW_VERSION}, and nothing published satisfies ${CLIENT_RANGE}"
|
|
159
207
|
BLOCKERS+=("Cannot set CACHE_NAME: no published frugal-iot-client satisfies ${CLIENT_RANGE}.")
|
|
160
208
|
else
|
|
161
209
|
if [[ "$SW_VERSION" == "$CLIENT_WANTED" ]]; then
|
|
162
|
-
|
|
210
|
+
say " CACHE_NAME is ${SW_VERSION}, matching the client this release installs"
|
|
163
211
|
else
|
|
164
212
|
# An in-place edit rather than a warning: there is only one right answer, and a stale cache
|
|
165
213
|
# name is invisible until somebody's browser keeps serving them last month's dashboard
|
|
@@ -174,16 +222,19 @@ else
|
|
|
174
222
|
fi
|
|
175
223
|
|
|
176
224
|
# ---- What would and would not be published ----
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if ! node scripts/check-published-files.js; then
|
|
225
|
+
say
|
|
226
|
+
say "=== Checking what the package would contain ==="
|
|
227
|
+
if ! FILESOUT=$(node scripts/check-published-files.js 2>&1); then
|
|
228
|
+
print -r -- "$FILESOUT"
|
|
180
229
|
BLOCKERS+=("Something git ignores would be published - see above.")
|
|
230
|
+
else
|
|
231
|
+
say "$FILESOUT"
|
|
181
232
|
fi
|
|
182
233
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
234
|
+
say
|
|
235
|
+
say "=== Reminders ==="
|
|
236
|
+
say " - Is the version in package.json the one you mean to publish? $(node -e 'console.log(require("./package.json").version)')"
|
|
237
|
+
say " - Nothing here is committed or published - do that yourself."
|
|
187
238
|
|
|
188
239
|
if (( ${#WARNINGS} )); then
|
|
189
240
|
echo
|