adapt-authoring-core 2.5.0 → 3.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/.github/workflows/releases.yml +9 -24
- package/conf/config.schema.json +16 -0
- package/conf/deprecated-lang.json +4 -0
- package/conf/deprecated-logger.json +7 -0
- package/docs/configure-environment.md +78 -0
- package/docs/developer-workflow.md +649 -0
- package/docs/error-handling.md +49 -0
- package/docs/plugins/configuration.js +75 -0
- package/docs/plugins/configuration.md +14 -0
- package/docs/plugins/errors.js +22 -0
- package/docs/plugins/errorsref.md +9 -0
- package/errors/errors.json +87 -0
- package/errors/node-core.json +39 -0
- package/index.js +5 -0
- package/lib/AbstractModule.js +3 -13
- package/lib/AdaptError.js +57 -0
- package/lib/App.js +66 -51
- package/lib/Config.js +226 -0
- package/lib/DataCache.js +6 -0
- package/lib/DependencyLoader.js +46 -103
- package/lib/Errors.js +50 -0
- package/lib/Hook.js +2 -3
- package/lib/Lang.js +126 -0
- package/lib/Logger.js +149 -0
- package/lib/utils/getArgs.js +4 -6
- package/migrations/3.0.0-conf-migrate-lang-config.js +7 -0
- package/migrations/3.0.0-conf-migrate-logger-config.js +9 -0
- package/package.json +8 -25
- package/tests/AbstractModule.spec.js +4 -54
- package/tests/AdaptError.spec.js +62 -0
- package/tests/Config.spec.js +122 -0
- package/tests/DataCache.spec.js +84 -1
- package/tests/DependencyLoader.spec.js +61 -146
- package/tests/Errors.spec.js +91 -0
- package/tests/Lang.spec.js +116 -0
- package/tests/Logger.spec.js +187 -0
- package/tests/utils-getArgs.spec.js +2 -8
- package/tests/App.spec.js +0 -160
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
# Developer workflow
|
|
2
|
+
|
|
3
|
+
This guide covers day-to-day development workflows for the Adapt authoring tool, with a focus on managing parallel streams of work across the multi-repo architecture.
|
|
4
|
+
|
|
5
|
+
## Quick navigation
|
|
6
|
+
|
|
7
|
+
- [How the project is structured](#how-the-project-is-structured)
|
|
8
|
+
- [Local development environment](#local-development-environment)
|
|
9
|
+
- [Workflow 1: Single-module bug fix](#workflow-1-single-module-bug-fix)
|
|
10
|
+
- [Workflow 2: Multi-module feature](#workflow-2-multi-module-feature)
|
|
11
|
+
- [Workflow 3: Hotfix during feature work](#workflow-3-hotfix-during-feature-work)
|
|
12
|
+
- [Workflow 4: Patching an older release line](#workflow-4-patching-an-older-release-line)
|
|
13
|
+
- [Coordinating releases](#coordinating-releases)
|
|
14
|
+
- [Version pinning strategy](#version-pinning-strategy)
|
|
15
|
+
- [Common pitfalls](#common-pitfalls)
|
|
16
|
+
|
|
17
|
+
## How the project is structured
|
|
18
|
+
|
|
19
|
+
Understanding the release pipeline is essential to choosing the right workflow.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────────────────────────────┐
|
|
23
|
+
│ adapt-authoring (parent) │
|
|
24
|
+
│ ├── Depends on 43+ adapt-authoring-* modules │
|
|
25
|
+
│ ├── Released manually via workflow_dispatch │
|
|
26
|
+
│ └── Runs integration tests across all modules │
|
|
27
|
+
│ │
|
|
28
|
+
│ Each module (api/, core/, auth/, server/, etc.) │
|
|
29
|
+
│ ├── Is a separate git repository │
|
|
30
|
+
│ ├── Has its own CI (linting, tests, releases) │
|
|
31
|
+
│ ├── Releases automatically on merge to master │
|
|
32
|
+
│ └── Published to npm or GitHub Packages │
|
|
33
|
+
└─────────────────────────────────────────────────────────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Key implication:** Merging a PR to a module's `master` branch triggers an immediate release via semantic-release. There is no staging step between merge and publish. This makes merge timing critical when coordinating multi-module changes.
|
|
37
|
+
|
|
38
|
+
## Local development environment
|
|
39
|
+
|
|
40
|
+
### Directory layout
|
|
41
|
+
|
|
42
|
+
The recommended local setup keeps all repos in a single parent directory:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
AAT/
|
|
46
|
+
├── adapt-authoring/ # Parent application (npm workspaces)
|
|
47
|
+
├── api/ # adapt-authoring-api (own git repo)
|
|
48
|
+
├── auth/ # adapt-authoring-auth (own git repo)
|
|
49
|
+
├── core/ # adapt-authoring-core (own git repo)
|
|
50
|
+
├── mongodb/ # adapt-authoring-mongodb (own git repo)
|
|
51
|
+
├── server/ # adapt-authoring-server (own git repo)
|
|
52
|
+
├── ... # 40+ more module repos
|
|
53
|
+
└── adapt-authoring/
|
|
54
|
+
└── local_adapt_modules/ # Symlinks or clones used by workspaces
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### How local testing works
|
|
58
|
+
|
|
59
|
+
The parent `adapt-authoring/` uses npm workspaces pointed at `local_adapt_modules/*`. When a module is present in that directory, npm resolves it locally instead of from the registry. This means you can test changes to any module without publishing first.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Link a module for local development
|
|
63
|
+
cd adapt-authoring/local_adapt_modules
|
|
64
|
+
ln -s ../../api adapt-authoring-api
|
|
65
|
+
|
|
66
|
+
# Reinstall to pick up the local link
|
|
67
|
+
cd ..
|
|
68
|
+
npm install --legacy-peer-deps
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Running the application locally
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cd adapt-authoring
|
|
75
|
+
npm start # Production mode
|
|
76
|
+
npm run debug # With --inspect flag for debugger
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Running tests
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Integration tests (from parent — boots app + MongoDB)
|
|
83
|
+
cd adapt-authoring
|
|
84
|
+
npm test
|
|
85
|
+
|
|
86
|
+
# Unit tests (from a specific module)
|
|
87
|
+
cd api
|
|
88
|
+
npm test # Runs: node --test tests/**/*.spec.js
|
|
89
|
+
|
|
90
|
+
# Linting (from any module)
|
|
91
|
+
npx standard
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Workflow 1: Single-module bug fix
|
|
95
|
+
|
|
96
|
+
This is the most common workflow. A bug exists in one module and needs to be fixed and released.
|
|
97
|
+
|
|
98
|
+
### Steps
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# 1. Start from a clean master
|
|
102
|
+
cd api
|
|
103
|
+
git checkout master && git pull origin master
|
|
104
|
+
|
|
105
|
+
# 2. Create a branch
|
|
106
|
+
git checkout -b issue/1234
|
|
107
|
+
|
|
108
|
+
# 3. Make changes, lint, and test
|
|
109
|
+
# ... edit files ...
|
|
110
|
+
npx standard
|
|
111
|
+
npm test
|
|
112
|
+
|
|
113
|
+
# 4. Commit with the correct prefix
|
|
114
|
+
git commit -m "Fix: Prevent crash when uploading empty file (fixes #1234)"
|
|
115
|
+
|
|
116
|
+
# 5. Push and create PR
|
|
117
|
+
git push -u origin issue/1234
|
|
118
|
+
gh pr create --title "Fix: Prevent crash when uploading empty file" \
|
|
119
|
+
--body "### Fixes #1234
|
|
120
|
+
|
|
121
|
+
### Fix
|
|
122
|
+
* Prevent crash when req.file is undefined on upload endpoint
|
|
123
|
+
|
|
124
|
+
### Testing
|
|
125
|
+
1. Upload an empty file via the UI
|
|
126
|
+
2. Verify no 500 error"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### What happens after merge
|
|
130
|
+
|
|
131
|
+
1. GitHub Actions runs semantic-release on the module
|
|
132
|
+
2. A new patch version is published (e.g. `3.3.0` → `3.3.1`)
|
|
133
|
+
3. The parent `adapt-authoring` repo still pins the old exact version — **the fix does not reach production until you explicitly bump it**
|
|
134
|
+
4. A main repo release (manual) picks up the new version when you're ready
|
|
135
|
+
|
|
136
|
+
This is by design. The parent uses [exact version pinning](#version-pinning-strategy), so no module update reaches production until a developer deliberately updates `package.json`.
|
|
137
|
+
|
|
138
|
+
### Getting the fix to production
|
|
139
|
+
|
|
140
|
+
Update the pinned version in the parent to the newly released patch:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
cd adapt-authoring
|
|
144
|
+
git checkout master && git pull origin master
|
|
145
|
+
|
|
146
|
+
# Update the dependency to the specific patch version
|
|
147
|
+
npm install adapt-authoring-api@3.3.1 --save-exact --legacy-peer-deps
|
|
148
|
+
|
|
149
|
+
# Commit the dependency bump
|
|
150
|
+
git add package.json package-lock.json
|
|
151
|
+
git commit -m "Upgrade: Bump adapt-authoring-api to v3.3.1 (refs #1234)"
|
|
152
|
+
git push origin master
|
|
153
|
+
|
|
154
|
+
# Trigger the release workflow from GitHub Actions UI
|
|
155
|
+
# or via CLI:
|
|
156
|
+
gh workflow run release.yml
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Because versions are pinned exactly, this bump includes *only* the bugfix — no unrelated features are pulled in.
|
|
160
|
+
|
|
161
|
+
## Workflow 2: Multi-module feature
|
|
162
|
+
|
|
163
|
+
A new feature spans multiple modules — for example, adding batch export support might touch `content`, `assets`, `api`, and `ui`.
|
|
164
|
+
|
|
165
|
+
### Phase 1: Branch all affected modules
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Create feature branches in each affected module
|
|
169
|
+
for module in content assets api ui; do
|
|
170
|
+
cd /path/to/AAT/$module
|
|
171
|
+
git checkout master && git pull origin master
|
|
172
|
+
git checkout -b feature/batch-export
|
|
173
|
+
done
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Use a consistent branch name across all modules — this makes it obvious which branches belong together.
|
|
177
|
+
|
|
178
|
+
### Phase 2: Develop and test locally
|
|
179
|
+
|
|
180
|
+
Work across the modules as needed. The local workspace setup means all your changes are picked up without publishing:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Run the full application with all your local changes
|
|
184
|
+
cd adapt-authoring
|
|
185
|
+
npm start
|
|
186
|
+
|
|
187
|
+
# Run integration tests against your combined changes
|
|
188
|
+
npm test
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Important:** Regularly test your feature branches against `master` of all other modules. This catches integration issues early and prevents surprises at merge time.
|
|
192
|
+
|
|
193
|
+
### Phase 3: Create PRs — but do not merge yet
|
|
194
|
+
|
|
195
|
+
Create a PR in each affected module. Use the PR description to cross-reference the related PRs:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
cd content
|
|
199
|
+
git push -u origin feature/batch-export
|
|
200
|
+
gh pr create --title "New: Add batch export support" \
|
|
201
|
+
--body "### Fixes #42
|
|
202
|
+
|
|
203
|
+
Part of the batch export feature. Related PRs:
|
|
204
|
+
- <org>/<module-assets-repo>#15
|
|
205
|
+
- <org>/<module-api-repo>#28
|
|
206
|
+
- <org>/<module-ui-repo>#33
|
|
207
|
+
|
|
208
|
+
### New
|
|
209
|
+
* Add batch selection to content schema
|
|
210
|
+
|
|
211
|
+
### Testing
|
|
212
|
+
1. Select multiple items and trigger export
|
|
213
|
+
2. Verify export completes with all items"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Phase 4: Review and approve all PRs
|
|
217
|
+
|
|
218
|
+
Get all PRs reviewed and approved, but **do not merge them individually**. Merging one module triggers a release of that module, and the feature may not work without the other modules being updated simultaneously.
|
|
219
|
+
|
|
220
|
+
### Phase 5: Merge in dependency order
|
|
221
|
+
|
|
222
|
+
Once all PRs are approved and CI is green, merge them in dependency order — modules that others depend on should be merged first:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
1. core (if changed) — foundational, no module deps
|
|
226
|
+
2. api — depends on core
|
|
227
|
+
3. assets — depends on api
|
|
228
|
+
4. content — depends on assets, api
|
|
229
|
+
5. ui — depends on api
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Wait for each module's release workflow to complete before merging the next, so that downstream modules can resolve the new version as a peer dependency.
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Check that the release completed
|
|
236
|
+
gh run list --repo <org>/adapt-authoring-api --limit 3
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Phase 6: Update and release the parent
|
|
240
|
+
|
|
241
|
+
Update the parent to pin the exact new versions of each changed module:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
cd adapt-authoring
|
|
245
|
+
git checkout master && git pull origin master
|
|
246
|
+
|
|
247
|
+
# Update all changed dependencies to their exact new versions
|
|
248
|
+
npm install adapt-authoring-api@3.5.0 \
|
|
249
|
+
adapt-authoring-assets@1.6.0 \
|
|
250
|
+
adapt-authoring-content@1.1.0 \
|
|
251
|
+
adapt-authoring-ui@2.1.0 \
|
|
252
|
+
--save-exact --legacy-peer-deps
|
|
253
|
+
|
|
254
|
+
# Run integration tests
|
|
255
|
+
npm test
|
|
256
|
+
|
|
257
|
+
# Commit and push
|
|
258
|
+
git add package.json package-lock.json
|
|
259
|
+
git commit -m "New: Add batch export support (fixes #100)"
|
|
260
|
+
git push origin master
|
|
261
|
+
|
|
262
|
+
# Trigger release
|
|
263
|
+
gh workflow run release.yml
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Workflow 3: Hotfix during feature work
|
|
267
|
+
|
|
268
|
+
You're midway through a multi-module feature when a critical bug is reported in production. Here's how to handle it without disrupting your feature work.
|
|
269
|
+
|
|
270
|
+
### The situation
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
You are working on:
|
|
274
|
+
content → branch: feature/batch-export
|
|
275
|
+
assets → branch: feature/batch-export
|
|
276
|
+
api → branch: feature/batch-export
|
|
277
|
+
|
|
278
|
+
Bug reported in: api (unrelated to your feature)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Step 1: Fix the bug on a separate branch
|
|
282
|
+
|
|
283
|
+
The multi-repo structure helps here — the bug is in one module and your feature branches are unaffected:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
cd api
|
|
287
|
+
|
|
288
|
+
# Stash or commit any in-progress feature work
|
|
289
|
+
git stash # or commit to the feature branch
|
|
290
|
+
|
|
291
|
+
# Create a hotfix branch from master
|
|
292
|
+
git checkout master && git pull origin master
|
|
293
|
+
git checkout -b issue/5678
|
|
294
|
+
|
|
295
|
+
# Fix the bug
|
|
296
|
+
# ... edit files ...
|
|
297
|
+
npx standard
|
|
298
|
+
npm test
|
|
299
|
+
|
|
300
|
+
# Commit and PR
|
|
301
|
+
git commit -m "Fix: Correct pagination offset in list endpoint (fixes #5678)"
|
|
302
|
+
git push -u origin issue/5678
|
|
303
|
+
gh pr create --title "Fix: Correct pagination offset in list endpoint" \
|
|
304
|
+
--body "### Fixes #5678
|
|
305
|
+
|
|
306
|
+
### Fix
|
|
307
|
+
* Off-by-one error in pagination calculation
|
|
308
|
+
|
|
309
|
+
### Testing
|
|
310
|
+
1. Request page 2 of any list endpoint
|
|
311
|
+
2. Verify correct items are returned"
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Step 2: Get the fix merged and released
|
|
315
|
+
|
|
316
|
+
Follow the normal review process. Once merged, semantic-release publishes the patch.
|
|
317
|
+
|
|
318
|
+
### Step 3: Push the fix to production
|
|
319
|
+
|
|
320
|
+
Because versions are pinned exactly, you can bump *only* the patched module — no unreleased features from other modules are pulled in:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
cd adapt-authoring
|
|
324
|
+
git checkout master && git pull origin master
|
|
325
|
+
npm install adapt-authoring-api@3.3.1 --save-exact --legacy-peer-deps
|
|
326
|
+
git add package.json package-lock.json
|
|
327
|
+
git commit -m "Upgrade: Bump adapt-authoring-api to v3.3.1 (fixes #5678)"
|
|
328
|
+
git push origin master
|
|
329
|
+
gh workflow run release.yml
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Step 4: Rebase your feature branch
|
|
333
|
+
|
|
334
|
+
Back on your feature work, rebase to pick up the hotfix (especially important if your feature touches the same module):
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
cd api
|
|
338
|
+
git checkout feature/batch-export
|
|
339
|
+
git rebase master
|
|
340
|
+
|
|
341
|
+
# If you stashed earlier
|
|
342
|
+
git stash pop
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
If there are no conflicts, you're done. If there are conflicts, resolve them — the fix is now part of your feature branch's history.
|
|
346
|
+
|
|
347
|
+
### Step 5: Test the feature against the updated master
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
cd adapt-authoring
|
|
351
|
+
npm test
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
This ensures your feature still works with the hotfix in place.
|
|
355
|
+
|
|
356
|
+
## Workflow 4: Patching an older release line
|
|
357
|
+
|
|
358
|
+
A feature has been developed and released as a new minor version (`v1.1.0`), but production is still pinned to `v1.0.0`. A bug is now found that needs fixing in the `v1.0.x` line *without* including the `v1.1.0` changes. The fix also needs to be in `v1.1.x` going forward.
|
|
359
|
+
|
|
360
|
+
**This requires two releases:** a patch on the old line (`v1.0.1`) and a patch on the current line (`v1.1.1`).
|
|
361
|
+
|
|
362
|
+
### Prerequisites: maintenance branch support
|
|
363
|
+
|
|
364
|
+
Modules need semantic-release configured to support maintenance branches. This is a one-time setup per module — see [Configuring maintenance branch support](#configuring-maintenance-branch-support) below.
|
|
365
|
+
|
|
366
|
+
### Step 1: Create the maintenance branch
|
|
367
|
+
|
|
368
|
+
Branch from the tag of the version you need to patch:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
cd my-module
|
|
372
|
+
git fetch --tags
|
|
373
|
+
git checkout -b 1.0.x v1.0.0
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
The branch name **must** match the pattern configured in semantic-release (e.g. `1.0.x`, `1.1.x`). This tells semantic-release to release within that version range.
|
|
377
|
+
|
|
378
|
+
### Step 2: Fix the bug on the maintenance branch
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
git checkout -b issue/5678 1.0.x
|
|
382
|
+
|
|
383
|
+
# Make the fix
|
|
384
|
+
npx standard
|
|
385
|
+
npm test
|
|
386
|
+
|
|
387
|
+
git commit -m "Fix: Handle null values in export (fixes #5678)"
|
|
388
|
+
git push -u origin issue/5678
|
|
389
|
+
|
|
390
|
+
# PR targets the 1.0.x branch, NOT master
|
|
391
|
+
gh pr create --base 1.0.x \
|
|
392
|
+
--title "Fix: Handle null values in export" \
|
|
393
|
+
--body "### Fixes #5678
|
|
394
|
+
|
|
395
|
+
### Fix
|
|
396
|
+
* Guard against null values during content export
|
|
397
|
+
|
|
398
|
+
### Testing
|
|
399
|
+
1. Export a course with empty optional fields
|
|
400
|
+
2. Verify export completes without error"
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Step 3: Merge and release v1.0.1
|
|
404
|
+
|
|
405
|
+
Once the PR is reviewed and merged into the `1.0.x` branch, semantic-release runs and publishes `v1.0.1`. This version contains only the `v1.0.0` code plus the bugfix — no `v1.1.0` feature code.
|
|
406
|
+
|
|
407
|
+
Push the maintenance branch to trigger the release:
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
# The 1.0.x branch needs to exist on the remote for the workflow to trigger
|
|
411
|
+
git push origin 1.0.x
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Step 4: Update production
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
cd adapt-authoring
|
|
418
|
+
git checkout master && git pull origin master
|
|
419
|
+
npm install adapt-authoring-mymodule@1.0.1 --save-exact --legacy-peer-deps
|
|
420
|
+
git add package.json package-lock.json
|
|
421
|
+
git commit -m "Upgrade: Bump adapt-authoring-mymodule to v1.0.1 (fixes #5678)"
|
|
422
|
+
git push origin master
|
|
423
|
+
gh workflow run release.yml
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Production now has the bugfix without the feature.
|
|
427
|
+
|
|
428
|
+
### Step 5: Cherry-pick the fix to master
|
|
429
|
+
|
|
430
|
+
The fix also needs to be in the `v1.1.x` line. Cherry-pick it onto master:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
cd my-module
|
|
434
|
+
git checkout master && git pull origin master
|
|
435
|
+
git checkout -b issue/5678-forward
|
|
436
|
+
|
|
437
|
+
# Cherry-pick the fix commit (use the actual SHA)
|
|
438
|
+
git cherry-pick <commit-sha>
|
|
439
|
+
|
|
440
|
+
# Resolve conflicts if any — the code may have changed between v1.0.0 and v1.1.0
|
|
441
|
+
npx standard
|
|
442
|
+
npm test
|
|
443
|
+
|
|
444
|
+
git push -u origin issue/5678-forward
|
|
445
|
+
gh pr create --title "Fix: Handle null values in export (fixes #5678)" \
|
|
446
|
+
--body "### Fixes #5678
|
|
447
|
+
|
|
448
|
+
Cherry-pick of the fix from the 1.0.x maintenance branch.
|
|
449
|
+
|
|
450
|
+
### Fix
|
|
451
|
+
* Guard against null values during content export"
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Once merged to master, semantic-release publishes `v1.1.1` with the fix included.
|
|
455
|
+
|
|
456
|
+
### Visual timeline
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
master: v1.0.0 ──── New: feature ──── v1.1.0 ──── cherry-pick fix ──── v1.1.1
|
|
460
|
+
\ ↑
|
|
461
|
+
1.0.x: └──────────────── Fix: bug ──── v1.0.1 │
|
|
462
|
+
│ │
|
|
463
|
+
└────────────────────┘
|
|
464
|
+
(same fix, both lines)
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Configuring maintenance branch support
|
|
468
|
+
|
|
469
|
+
This is a one-time setup per module. Two files need updating:
|
|
470
|
+
|
|
471
|
+
**1. `package.json` — add `branches` to the release config:**
|
|
472
|
+
|
|
473
|
+
```json
|
|
474
|
+
"release": {
|
|
475
|
+
"branches": [
|
|
476
|
+
"+([0-9]).+([0-9]).x",
|
|
477
|
+
"master"
|
|
478
|
+
],
|
|
479
|
+
"plugins": [
|
|
480
|
+
["@semantic-release/commit-analyzer", { "preset": "eslint" }],
|
|
481
|
+
["@semantic-release/release-notes-generator", { "preset": "eslint" }],
|
|
482
|
+
"@semantic-release/npm",
|
|
483
|
+
"@semantic-release/github",
|
|
484
|
+
"@semantic-release/git"
|
|
485
|
+
]
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
The `+([0-9]).+([0-9]).x` pattern matches branches like `1.0.x`, `2.3.x`, etc. The `master` entry keeps the existing behaviour for normal releases. Order matters — maintenance branches must come before the main branch.
|
|
490
|
+
|
|
491
|
+
**2. `.github/workflows/releases.yml` — trigger on maintenance branches:**
|
|
492
|
+
|
|
493
|
+
```yaml
|
|
494
|
+
name: Release
|
|
495
|
+
on:
|
|
496
|
+
push:
|
|
497
|
+
branches:
|
|
498
|
+
- master
|
|
499
|
+
- '+([0-9]).+([0-9]).x'
|
|
500
|
+
|
|
501
|
+
jobs:
|
|
502
|
+
release:
|
|
503
|
+
name: Release
|
|
504
|
+
runs-on: ubuntu-latest
|
|
505
|
+
permissions:
|
|
506
|
+
contents: write
|
|
507
|
+
issues: write
|
|
508
|
+
pull-requests: write
|
|
509
|
+
id-token: write
|
|
510
|
+
steps:
|
|
511
|
+
- name: Checkout
|
|
512
|
+
uses: actions/checkout@v3
|
|
513
|
+
with:
|
|
514
|
+
fetch-depth: 0
|
|
515
|
+
- name: Setup Node.js
|
|
516
|
+
uses: actions/setup-node@v3
|
|
517
|
+
with:
|
|
518
|
+
node-version: 'lts/*'
|
|
519
|
+
- name: Update npm
|
|
520
|
+
run: npm install -g npm@latest
|
|
521
|
+
- name: Install dependencies
|
|
522
|
+
run: npm install
|
|
523
|
+
- name: Release
|
|
524
|
+
env:
|
|
525
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
526
|
+
run: npx semantic-release
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
The key change is adding `'+([0-9]).+([0-9]).x'` to the `branches` trigger so pushes to maintenance branches also run the release workflow.
|
|
530
|
+
|
|
531
|
+
### When to create maintenance branches
|
|
532
|
+
|
|
533
|
+
Don't create maintenance branches preemptively. Create them only when you actually need to patch an older release line. Once a maintenance branch is no longer needed (e.g. production has moved to `v1.1.x`), it can be left as-is — it won't interfere with future releases.
|
|
534
|
+
|
|
535
|
+
### Cleaning up maintenance branches
|
|
536
|
+
|
|
537
|
+
Maintenance branches can be deleted from the remote once production has moved past that version line:
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
# Only after production is on v1.1.x or later
|
|
541
|
+
git push origin --delete 1.0.x
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
## Coordinating releases
|
|
545
|
+
|
|
546
|
+
### Module releases (automatic)
|
|
547
|
+
|
|
548
|
+
Every merge to a module's `master` (or a maintenance branch like `1.0.x`) triggers semantic-release. The commit message prefix determines the version bump:
|
|
549
|
+
|
|
550
|
+
| Merge contains | Version bump | Example |
|
|
551
|
+
|---|---|---|
|
|
552
|
+
| `Fix:` commits only | Patch (0.0.x) | 3.3.0 → 3.3.1 |
|
|
553
|
+
| `Update:` or `New:` | Minor (0.x.0) | 3.3.0 → 3.4.0 |
|
|
554
|
+
| `Breaking:` | Major (x.0.0) | 3.3.0 → 4.0.0 |
|
|
555
|
+
| `Docs:`, `Chore:` only | No release | — |
|
|
556
|
+
|
|
557
|
+
### Main repository releases (manual)
|
|
558
|
+
|
|
559
|
+
The parent `adapt-authoring` repo aggregates module updates and releases on its own cadence:
|
|
560
|
+
|
|
561
|
+
1. Module releases accumulate on npm/GitHub Packages
|
|
562
|
+
2. A developer updates `package.json` in the parent to reference new versions
|
|
563
|
+
3. Integration tests run to validate the combination
|
|
564
|
+
4. The release workflow is triggered manually via `gh workflow run release.yml`
|
|
565
|
+
5. `at-utils version-check` determines the appropriate version bump based on which modules changed
|
|
566
|
+
|
|
567
|
+
### When to release the parent
|
|
568
|
+
|
|
569
|
+
- **After a hotfix:** Immediately, to get the fix to production
|
|
570
|
+
- **After a feature:** Once all module PRs are merged, their releases complete, and integration tests pass
|
|
571
|
+
- **On a regular cadence:** Periodically, to batch smaller improvements
|
|
572
|
+
|
|
573
|
+
## Version pinning strategy
|
|
574
|
+
|
|
575
|
+
The parent `adapt-authoring/package.json` uses **exact version pinning** (no `^` or `~` prefix) for all module dependencies. This is the foundation that makes the hotfix-during-feature-work workflow safe.
|
|
576
|
+
|
|
577
|
+
### Why exact versions?
|
|
578
|
+
|
|
579
|
+
The problem with caret ranges (`^`):
|
|
580
|
+
|
|
581
|
+
```
|
|
582
|
+
Timeline:
|
|
583
|
+
1. adapt-authoring-api is at 3.3.0 in production
|
|
584
|
+
2. Developer ships a new feature → api 3.4.0 released to npm
|
|
585
|
+
3. A bug is found in production
|
|
586
|
+
4. Developer fixes the bug → api 3.4.1 released to npm
|
|
587
|
+
5. Production site runs npm ci...
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
With `"adapt-authoring-api": "^3.3.0"`, step 5 installs `3.4.1` — which includes the untested feature from step 2 alongside the bugfix. The production site gets changes that haven't been through a full release cycle.
|
|
591
|
+
|
|
592
|
+
With `"adapt-authoring-api": "3.3.0"` (exact), step 5 installs exactly `3.3.0`. The developer then explicitly bumps to `3.3.1` (bugfix only) or `3.4.1` (bugfix + feature) depending on what's appropriate.
|
|
593
|
+
|
|
594
|
+
### How it works in practice
|
|
595
|
+
|
|
596
|
+
| Scenario | What to bump to | Command |
|
|
597
|
+
|---|---|---|
|
|
598
|
+
| Hotfix only | The patch version | `npm install adapt-authoring-api@3.3.1 --save-exact` |
|
|
599
|
+
| Feature ready for release | The minor version (includes all patches) | `npm install adapt-authoring-api@3.4.1 --save-exact` |
|
|
600
|
+
| Breaking change | The major version | `npm install adapt-authoring-api@4.0.0 --save-exact` |
|
|
601
|
+
|
|
602
|
+
### Rules
|
|
603
|
+
|
|
604
|
+
1. **Always use `--save-exact`** when updating dependencies in the parent repo (or set `save-exact=true` in `.npmrc`)
|
|
605
|
+
2. **Never use `@latest`** in the parent repo — always specify the exact version you intend to deploy
|
|
606
|
+
3. **One concern per bump** — if you're shipping a hotfix, only bump the module that was patched; don't bundle in other module updates
|
|
607
|
+
4. **Batch feature updates** into planned releases — update multiple modules together only when you're ready to test and release the full set
|
|
608
|
+
|
|
609
|
+
### What about peer dependencies in modules?
|
|
610
|
+
|
|
611
|
+
Module-to-module peer dependencies (e.g. `"adapt-authoring-core": "^2.0.0"` in `adapt-authoring-api/package.json`) should continue using caret ranges. These express compatibility ranges, not deployment targets. The parent's exact pins determine what actually runs together in production.
|
|
612
|
+
|
|
613
|
+
## Common pitfalls
|
|
614
|
+
|
|
615
|
+
### Shipping unreleased features with a hotfix
|
|
616
|
+
|
|
617
|
+
**Problem:** A module has a new feature (minor release) and a subsequent bugfix (patch release). You bump the parent to get the bugfix, but the new feature comes along for the ride because both are on `master`.
|
|
618
|
+
|
|
619
|
+
**Solution:** This is exactly why the parent uses exact version pinning. When bumping for a hotfix, specify the patch version explicitly (`3.3.1`), not `@latest`. If the bugfix was released *after* a feature (i.e. `master` already has `v3.4.0` and you need to patch `v3.3.0`), use a [maintenance branch](#workflow-4-patching-an-older-release-line) to release `v3.3.1` from the old line. In most cases, plan your merge order so that hotfixes land before features on the module's master branch.
|
|
620
|
+
|
|
621
|
+
### Merging a multi-module feature one PR at a time
|
|
622
|
+
|
|
623
|
+
**Problem:** You merge the API changes but not the UI changes. The API release publishes a new version that the UI hasn't been updated to work with. Users who update get a broken combination.
|
|
624
|
+
|
|
625
|
+
**Solution:** Merge in dependency order, wait for each release, and don't leave partial features merged overnight.
|
|
626
|
+
|
|
627
|
+
### Forgetting to rebase after a hotfix
|
|
628
|
+
|
|
629
|
+
**Problem:** Your feature branch diverges from master after a hotfix lands. When you eventually merge, you may introduce conflicts or accidentally revert the hotfix.
|
|
630
|
+
|
|
631
|
+
**Solution:** Rebase feature branches promptly after any hotfix to the same module.
|
|
632
|
+
|
|
633
|
+
### Committing with the wrong prefix
|
|
634
|
+
|
|
635
|
+
**Problem:** A `Fix:` commit triggers a patch release, but your change is actually a breaking change. Consumers don't expect a major API shift in a patch version.
|
|
636
|
+
|
|
637
|
+
**Solution:** Double-check your commit prefix. Use `Breaking:` for anything that changes existing behaviour in a non-backwards-compatible way. See [commit message guidelines](contributing-code#commit-messages) for details.
|
|
638
|
+
|
|
639
|
+
### Testing only the module, not the integration
|
|
640
|
+
|
|
641
|
+
**Problem:** Module-level tests pass, but the feature breaks when modules interact.
|
|
642
|
+
|
|
643
|
+
**Solution:** Always run `npm test` from the parent `adapt-authoring/` directory before marking your work as ready for review. This runs integration tests that boot the full application.
|
|
644
|
+
|
|
645
|
+
### Publishing a dependency bump without testing
|
|
646
|
+
|
|
647
|
+
**Problem:** You update `adapt-authoring/package.json` to reference new module versions and release without running integration tests. A subtle incompatibility ships.
|
|
648
|
+
|
|
649
|
+
**Solution:** Always run integration tests locally (or let CI run them) before triggering the parent release workflow.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Error handling
|
|
2
|
+
Handling errors correctly is a key aspect of writing stable software. This guide will give some tips on how you should deal with errors, as well as the utilities available to make error handling simpler.
|
|
3
|
+
|
|
4
|
+
Before going into specifics, it would be useful to discuss application errors in general terms. The errors you experience are likely to fall into one of the following broad groups:
|
|
5
|
+
- **Initialisation errors**: i.e. problems during start-up
|
|
6
|
+
- **General server errors**: errors which occur outside of user requests, possibly during automated tasks
|
|
7
|
+
- **User errors**: errors which are a direct result of a user request
|
|
8
|
+
|
|
9
|
+
You will need to deal with each category of error differently. Below are some general tips on handling each type of error.
|
|
10
|
+
|
|
11
|
+
## Initialisation errors
|
|
12
|
+
Any errors which occur during initialisation should be captured and logged as appropriate. Depending on the type of error, it may or may not be considered fatal to your code.
|
|
13
|
+
|
|
14
|
+
Some examples:
|
|
15
|
+
- For a database-handler module, failing to connect to the database would be considered a fatal error, as no further actions can be executed. In this case, the code should perform any clean-up and exit.
|
|
16
|
+
- For a configuration module, failing to load the user configuration file may not be fatal if the application can run without it (e.g. with default settings). In this case the error should be logged, but the code can continue to initialise post-error.
|
|
17
|
+
- For a module which attempts to load a specific file in each module connected to the core system, failing to load a single configuration file may not be an error as such, but rather an expected outcome if the configuration file in question is not something that's required to be defined for every module. In this case, the code can continue and it may not even be necessary to log a message.
|
|
18
|
+
|
|
19
|
+
## General server errors
|
|
20
|
+
'General server errors' is a broad category which covers other errors that don't take place at either initialisation or as a result of direct user action. Again, depending on the specific error, these may or may not be fatal.
|
|
21
|
+
|
|
22
|
+
Some examples:
|
|
23
|
+
- For a database-handler module, disconnecting from the database is an expected error, and can be handled and rectified easily.
|
|
24
|
+
|
|
25
|
+
## User errors
|
|
26
|
+
User errors are any errors which are caused as a direct result of a user performing an action incorrectly. It is even *more* critical with user errors that the error is as specific and descriptive as possible, as the response needs to be informative and instructive to the user that caused the error. Failing to do so will result in an unpleasant user experience.
|
|
27
|
+
|
|
28
|
+
Some examples:
|
|
29
|
+
- A user uploads a file in an invalid format. This definitely isn't a fatal error, as the code can continue post-error. The returned error should inform the user of the issue, as well as how it can be rectified.
|
|
30
|
+
|
|
31
|
+
## Defining errors
|
|
32
|
+
Depending on the kinds of error that you're dealing with in your code, it may be useful to include a set of custom error definitions specific to your code.
|
|
33
|
+
|
|
34
|
+
Defining useful errors is a critical part of any software system. The error registry makes it easy to define errors for your own modules, and make use of errors defined in other modules.
|
|
35
|
+
|
|
36
|
+
## Catching errors
|
|
37
|
+
|
|
38
|
+
## Throwing errors
|
|
39
|
+
As mentioned above, it is preferable to catch errors internally in your code and re-throw these errors.
|
|
40
|
+
|
|
41
|
+
The error registry acts as a central store for all errors defined in the system, and errors can be accessed and thrown from here. For convenience, the errors registry is available directly as a property of the main App instance via `app.errors`:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
try {
|
|
45
|
+
// do something here
|
|
46
|
+
} catch(e) {
|
|
47
|
+
throw this.app.errors.MY_ERROR
|
|
48
|
+
}
|
|
49
|
+
```
|