backend-manager 5.2.14 → 5.2.15

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/CHANGELOG.md CHANGED
@@ -14,6 +14,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
14
14
  - `Fixed` for any bug fixes.
15
15
  - `Security` in case of vulnerabilities.
16
16
 
17
+ # [5.2.15] - 2026-05-28
18
+
19
+ ### Changed
20
+
21
+ - **Standardized the GitHub token env var `GITHUB_TOKEN` → `GH_TOKEN`** across the entire repo, to match the convention used in all other ITW repos. Updated every GitHub-backed route and action (`admin/post`, `content/post`, `admin/repo/content`, `general/fetch-post`, `admin/write-repo-content`, `admin/edit-post`, `admin/create-post`, legacy `create-post`), the email image-host library (`libraries/email/generators/lib/image-host.js`), the CLI deprecated-env notice, the `templates/_.env` scaffold, the `docs/marketing-campaigns.md` reference, and all related test files. This is a hard rename with no fallback — any environment (CI, prod, local `.env`) that still sets `GITHUB_TOKEN` must switch to `GH_TOKEN` for the GitHub-backed routes to work.
22
+
17
23
  # [5.2.14] - 2026-05-28
18
24
 
19
25
  ### Removed
@@ -208,7 +208,7 @@ Templates add their own fields on top (e.g. classic adds `intro` + `sections`; f
208
208
 
209
209
  This means the newsletter is never "stuck" — even with Beehiiv disabled or failing, you get an actionable email pointing to ready-to-paste assets. The alert is best-effort; failure to send is logged but does not block the Firestore campaign-doc write.
210
210
 
211
- Requires `GITHUB_TOKEN` env var (org-scoped, write access to `newsletter-assets`). Without it, the cron's HTML/image upload calls throw and the run aborts.
211
+ Requires `GH_TOKEN` env var (org-scoped, write access to `newsletter-assets`). Without it, the cron's HTML/image upload calls throw and the run aborts.
212
212
 
213
213
  ## Iteration test asset story
214
214
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.2.14",
3
+ "version": "5.2.15",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -36,7 +36,7 @@ class EnvRuntimeConfigDeprecatedTest extends BaseTest {
36
36
  ' The new format uses individual environment variables:\n' +
37
37
  ' BACKEND_MANAGER_KEY=\n' +
38
38
  ' BACKEND_MANAGER_NAMESPACE=\n' +
39
- ' GITHUB_TOKEN=\n' +
39
+ ' GH_TOKEN=\n' +
40
40
  ' Please update your .env file manually and remove RUNTIME_CONFIG.'
41
41
  );
42
42
  }
@@ -55,7 +55,7 @@ let Module = {
55
55
  return new Promise(async function(resolve, reject) {
56
56
  let finalPath = poster.removeDirDot(meta.finalPath);
57
57
  let tempPath = (meta.tempPath);
58
- await createFile(self.Manager.config?.github?.user, repoInfo.user, repoInfo.name, process.env.GITHUB_TOKEN, finalPath, await poster.readImage(tempPath))
58
+ await createFile(self.Manager.config?.github?.user, repoInfo.user, repoInfo.name, process.env.GH_TOKEN, finalPath, await poster.readImage(tempPath))
59
59
  .catch((e) => {
60
60
  // console.log('---CAUGHT 1', e);
61
61
  })
@@ -66,7 +66,7 @@ let Module = {
66
66
  let finalPost = await poster.create(assistant.request.data);
67
67
 
68
68
  // Save post OR commit
69
- await createFile(self.Manager.config?.github?.user, repoInfo.user, repoInfo.name, process.env.GITHUB_TOKEN, poster.removeDirDot(finalPost.path), finalPost.content)
69
+ await createFile(self.Manager.config?.github?.user, repoInfo.user, repoInfo.name, process.env.GH_TOKEN, poster.removeDirDot(finalPost.path), finalPost.content)
70
70
  .catch((e) => {
71
71
  response.status = 400;
72
72
  response.error = new Error('Failed to post: ' + e);
@@ -39,7 +39,7 @@ Module.prototype.main = function () {
39
39
 
40
40
  // Setup Octokit
41
41
  self.octokit = new Octokit({
42
- auth: process.env.GITHUB_TOKEN,
42
+ auth: process.env.GH_TOKEN,
43
43
  });
44
44
 
45
45
  // Check for required values
@@ -25,7 +25,7 @@ Module.prototype.main = function () {
25
25
  }
26
26
 
27
27
  // Check for GitHub configuration
28
- if (!process.env.GITHUB_TOKEN) {
28
+ if (!process.env.GH_TOKEN) {
29
29
  return reject(assistant.errorify(`GitHub API key not configured.`, {code: 500}));
30
30
  }
31
31
 
@@ -42,7 +42,7 @@ Module.prototype.main = function () {
42
42
 
43
43
  // Setup Octokit
44
44
  self.octokit = new Octokit({
45
- auth: process.env.GITHUB_TOKEN,
45
+ auth: process.env.GH_TOKEN,
46
46
  });
47
47
 
48
48
  // Check for required values
@@ -25,7 +25,7 @@ Module.prototype.main = function () {
25
25
  }
26
26
 
27
27
  // Check for GitHub configuration
28
- if (!process.env.GITHUB_TOKEN) {
28
+ if (!process.env.GH_TOKEN) {
29
29
  return reject(assistant.errorify(`GitHub API key not configured.`, {code: 500}));
30
30
  }
31
31
 
@@ -42,7 +42,7 @@ Module.prototype.main = function () {
42
42
 
43
43
  // Setup Octokit
44
44
  self.octokit = new Octokit({
45
- auth: process.env.GITHUB_TOKEN,
45
+ auth: process.env.GH_TOKEN,
46
46
  });
47
47
 
48
48
  // Check for required values
@@ -14,7 +14,7 @@ Module.prototype.main = function () {
14
14
 
15
15
  return new Promise(async function(resolve, reject) {
16
16
  // Check for GitHub configuration
17
- if (!process.env.GITHUB_TOKEN) {
17
+ if (!process.env.GH_TOKEN) {
18
18
  return reject(assistant.errorify(`GitHub API key not configured.`, {code: 500}));
19
19
  }
20
20
 
@@ -24,7 +24,7 @@ Module.prototype.main = function () {
24
24
 
25
25
  // Setup Octokit
26
26
  const octokit = new Octokit({
27
- auth: process.env.GITHUB_TOKEN,
27
+ auth: process.env.GH_TOKEN,
28
28
  });
29
29
 
30
30
  // Setup options
@@ -59,7 +59,7 @@ const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
59
59
  * @param {string} [args.subject] - Newsletter subject. Embedded in the commit message so
60
60
  * `git log` reads as a human-browseable history.
61
61
  * @param {string} [args.commitMessage] - Full override of the default commit message
62
- * @param {string} [args.token] - GitHub token (defaults to process.env.GITHUB_TOKEN)
62
+ * @param {string} [args.token] - GitHub token (defaults to process.env.GH_TOKEN)
63
63
  * @param {object} [args.assistant] - logger
64
64
  * @returns {Promise<{ urls: string[], paths: string[], htmlUrl?: string, htmlPath?: string, folderUrl: string, commitSha: string }>}
65
65
  */
@@ -76,10 +76,10 @@ async function uploadAssets({ images, html, markdown, summary, brandId, campaign
76
76
  validateBrandId(brandId);
77
77
  validateCampaignId(campaignId);
78
78
 
79
- const githubToken = token || process.env.GITHUB_TOKEN;
79
+ const githubToken = token || process.env.GH_TOKEN;
80
80
 
81
81
  if (!githubToken) {
82
- throw new Error('image-host: GITHUB_TOKEN env var (or token arg) is required');
82
+ throw new Error('image-host: GH_TOKEN env var (or token arg) is required');
83
83
  }
84
84
 
85
85
  const log = (msg) => assistant?.log ? assistant.log(`[image-host] ${msg}`) : null;
@@ -37,7 +37,7 @@ module.exports = async ({ assistant, Manager, user, settings, analytics }) => {
37
37
  }
38
38
 
39
39
  // Check for GitHub configuration
40
- if (!process.env.GITHUB_TOKEN) {
40
+ if (!process.env.GH_TOKEN) {
41
41
  return assistant.respond('GitHub API key not configured.', { code: 500 });
42
42
  }
43
43
 
@@ -52,7 +52,7 @@ module.exports = async ({ assistant, Manager, user, settings, analytics }) => {
52
52
 
53
53
  // Setup Octokit
54
54
  const octokit = new Octokit({
55
- auth: process.env.GITHUB_TOKEN,
55
+ auth: process.env.GH_TOKEN,
56
56
  });
57
57
 
58
58
  // Check for required values
@@ -20,7 +20,7 @@ module.exports = async ({ assistant, Manager, user, settings, analytics }) => {
20
20
  }
21
21
 
22
22
  // Check for GitHub configuration
23
- if (!process.env.GITHUB_TOKEN) {
23
+ if (!process.env.GH_TOKEN) {
24
24
  return assistant.respond('GitHub API key not configured.', { code: 500 });
25
25
  }
26
26
 
@@ -35,7 +35,7 @@ module.exports = async ({ assistant, Manager, user, settings, analytics }) => {
35
35
 
36
36
  // Setup Octokit
37
37
  const octokit = new Octokit({
38
- auth: process.env.GITHUB_TOKEN,
38
+ auth: process.env.GH_TOKEN,
39
39
  });
40
40
 
41
41
  // Check for required values
@@ -17,7 +17,7 @@ module.exports = async ({ assistant, Manager, user, settings, analytics }) => {
17
17
  }
18
18
 
19
19
  // Check for GitHub configuration
20
- if (!process.env.GITHUB_TOKEN) {
20
+ if (!process.env.GH_TOKEN) {
21
21
  return assistant.respond('GitHub API key not configured.', { code: 500 });
22
22
  }
23
23
 
@@ -31,7 +31,7 @@ module.exports = async ({ assistant, Manager, user, settings, analytics }) => {
31
31
 
32
32
  // Setup Octokit
33
33
  const octokit = new Octokit({
34
- auth: process.env.GITHUB_TOKEN,
34
+ auth: process.env.GH_TOKEN,
35
35
  });
36
36
 
37
37
  // Check for required values
@@ -8,7 +8,7 @@ const { parse } = require('yaml');
8
8
  module.exports = async ({ assistant, Manager, settings, analytics }) => {
9
9
 
10
10
  // Check for GitHub configuration
11
- if (!process.env.GITHUB_TOKEN) {
11
+ if (!process.env.GH_TOKEN) {
12
12
  return assistant.respond('GitHub API key not configured.', { code: 500 });
13
13
  }
14
14
 
@@ -18,7 +18,7 @@ module.exports = async ({ assistant, Manager, settings, analytics }) => {
18
18
 
19
19
  // Setup Octokit
20
20
  const octokit = new Octokit({
21
- auth: process.env.GITHUB_TOKEN,
21
+ auth: process.env.GH_TOKEN,
22
22
  });
23
23
 
24
24
  // Check for required parameters
package/templates/_.env CHANGED
@@ -1,4 +1,7 @@
1
1
  # ========== Default Values ==========
2
+ # GitHub
3
+ GH_TOKEN=""
4
+
2
5
  # Backend Manager
3
6
  BACKEND_MANAGER_KEY=""
4
7
  BACKEND_MANAGER_WEBHOOK_KEY=""
@@ -6,9 +9,6 @@ BACKEND_MANAGER_NAMESPACE=""
6
9
  BACKEND_MANAGER_OPENAI_API_KEY=""
7
10
  BACKEND_MANAGER_ANTHROPIC_API_KEY=""
8
11
 
9
- # GitHub
10
- GITHUB_TOKEN=""
11
-
12
12
  # AI
13
13
  OPENAI_API_KEY=""
14
14
  ANTHROPIC_API_KEY=""
@@ -112,7 +112,7 @@ module.exports = {
112
112
  name: 'create-test-post',
113
113
  auth: 'admin',
114
114
  timeout: 60000,
115
- skip: !process.env.GITHUB_TOKEN ? 'GITHUB_TOKEN env var not set' : false,
115
+ skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
116
116
 
117
117
  async run({ assert, state, config }) {
118
118
  if (!config.github?.repo_website) {
@@ -120,7 +120,7 @@ module.exports = {
120
120
  return;
121
121
  }
122
122
 
123
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
123
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
124
124
 
125
125
  // Parse owner/repo from githubRepoWebsite
126
126
  const repoMatch = config.github?.repo_website.match(/github\.com\/([^/]+)\/([^/]+)/);
@@ -181,7 +181,7 @@ module.exports = {
181
181
  name: 'verify-file-exists',
182
182
  auth: 'admin',
183
183
  timeout: 30000,
184
- skip: !process.env.GITHUB_TOKEN ? 'GITHUB_TOKEN env var not set' : false,
184
+ skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
185
185
 
186
186
  async run({ assert, state, config }) {
187
187
  if (!state.postPath) {
@@ -190,7 +190,7 @@ module.exports = {
190
190
 
191
191
  state.postUrl = `https://${config.domain}/blog/${state.postSlug}`;
192
192
 
193
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
193
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
194
194
 
195
195
  // Verify the file exists via direct API (not code search)
196
196
  const { data: fileData } = await octokit.rest.repos.getContent({
@@ -211,7 +211,7 @@ module.exports = {
211
211
  name: 'edit-test-post',
212
212
  auth: 'admin',
213
213
  timeout: 60000,
214
- skip: !process.env.GITHUB_TOKEN ? 'GITHUB_TOKEN env var not set' : false,
214
+ skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
215
215
 
216
216
  async run({ http, assert, state }) {
217
217
  if (!state.postUrl) {
@@ -244,7 +244,7 @@ module.exports = {
244
244
  name: 'verify-edit',
245
245
  auth: 'admin',
246
246
  timeout: 30000,
247
- skip: !process.env.GITHUB_TOKEN ? 'GITHUB_TOKEN env var not set' : false,
247
+ skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
248
248
 
249
249
  async run({ assert, state }) {
250
250
  if (!state.postPath || state.editSkipped) {
@@ -252,7 +252,7 @@ module.exports = {
252
252
  return;
253
253
  }
254
254
 
255
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
255
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
256
256
 
257
257
  // Fetch the file directly from GitHub
258
258
  const { data: fileData } = await octokit.rest.repos.getContent({
@@ -309,11 +309,11 @@ module.exports = {
309
309
  timeout: 60000,
310
310
 
311
311
  async run({ state }) {
312
- if (!process.env.GITHUB_TOKEN || !state.postPath) {
312
+ if (!process.env.GH_TOKEN || !state.postPath) {
313
313
  return;
314
314
  }
315
315
 
316
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
316
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
317
317
 
318
318
  // Get the current SHA (may have changed after edit)
319
319
  try {
@@ -4,7 +4,7 @@
4
4
  * Writes arbitrary content to a GitHub repository
5
5
  * Requires admin/blogger role, GitHub API key, and repo_website config
6
6
  *
7
- * IMPORTANT: These tests require GITHUB_TOKEN and github.repo_website to be configured.
7
+ * IMPORTANT: These tests require GH_TOKEN and github.repo_website to be configured.
8
8
  * If GitHub is not configured, the tests will fail.
9
9
  *
10
10
  * This is a suite because we need to clean up created files and cancel workflows after tests.
@@ -155,11 +155,11 @@ module.exports = {
155
155
  timeout: 60000,
156
156
 
157
157
  async run({ state, config }) {
158
- if (!process.env.GITHUB_TOKEN || !config.github?.repo_website) {
158
+ if (!process.env.GH_TOKEN || !config.github?.repo_website) {
159
159
  return;
160
160
  }
161
161
 
162
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
162
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
163
163
 
164
164
  // Parse owner/repo from githubRepoWebsite (e.g., 'https://github.com/owner/repo')
165
165
  const repoMatch = config.github?.repo_website.match(/github\.com\/([^/]+)\/([^/]+)/);
@@ -111,8 +111,8 @@ module.exports = {
111
111
  skip: !process.env.TEST_EXTENDED_MODE ? 'TEST_EXTENDED_MODE env var not set' : false,
112
112
 
113
113
  async run({ http, assert, state, config }) {
114
- if (!process.env.GITHUB_TOKEN) {
115
- assert.fail('GITHUB_TOKEN env var not set');
114
+ if (!process.env.GH_TOKEN) {
115
+ assert.fail('GH_TOKEN env var not set');
116
116
  return;
117
117
  }
118
118
 
@@ -167,7 +167,7 @@ module.exports = {
167
167
  return; // Previous test didn't run
168
168
  }
169
169
 
170
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
170
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
171
171
 
172
172
  // Fetch the committed post from GitHub
173
173
  const { data: fileData } = await octokit.rest.repos.getContent({
@@ -203,7 +203,7 @@ module.exports = {
203
203
  return; // Previous test didn't run
204
204
  }
205
205
 
206
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
206
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
207
207
  const imageDir = `src/assets/images/blog/post-${state.postId}/`;
208
208
 
209
209
  // List committed images and pick the header (matches the slugified URL "bem-test-create-post")
@@ -285,11 +285,11 @@ module.exports = {
285
285
  timeout: 60000,
286
286
 
287
287
  async run({ state }) {
288
- if (!process.env.GITHUB_TOKEN || !state.postPath) {
288
+ if (!process.env.GH_TOKEN || !state.postPath) {
289
289
  return;
290
290
  }
291
291
 
292
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
292
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
293
293
 
294
294
  // Delete the test post
295
295
  try {
@@ -114,7 +114,7 @@ module.exports = {
114
114
  name: 'create-test-post',
115
115
  auth: 'admin',
116
116
  timeout: 60000,
117
- skip: !process.env.GITHUB_TOKEN ? 'GITHUB_TOKEN env var not set' : false,
117
+ skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
118
118
 
119
119
  async run({ assert, state, config }) {
120
120
  if (!config.github?.repo_website) {
@@ -122,7 +122,7 @@ module.exports = {
122
122
  return;
123
123
  }
124
124
 
125
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
125
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
126
126
 
127
127
  // Parse owner/repo from githubRepoWebsite
128
128
  const repoMatch = config.github?.repo_website.match(/github\.com\/([^/]+)\/([^/]+)/);
@@ -183,7 +183,7 @@ module.exports = {
183
183
  name: 'verify-file-exists',
184
184
  auth: 'admin',
185
185
  timeout: 30000,
186
- skip: !process.env.GITHUB_TOKEN ? 'GITHUB_TOKEN env var not set' : false,
186
+ skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
187
187
 
188
188
  async run({ assert, state, config }) {
189
189
  if (!state.postPath) {
@@ -192,7 +192,7 @@ module.exports = {
192
192
 
193
193
  state.postUrl = `https://${config.domain}/blog/${state.postSlug}`;
194
194
 
195
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
195
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
196
196
 
197
197
  // Verify the file exists via direct API (not code search)
198
198
  const { data: fileData } = await octokit.rest.repos.getContent({
@@ -213,7 +213,7 @@ module.exports = {
213
213
  name: 'edit-test-post',
214
214
  auth: 'admin',
215
215
  timeout: 60000,
216
- skip: !process.env.GITHUB_TOKEN ? 'GITHUB_TOKEN env var not set' : false,
216
+ skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
217
217
 
218
218
  async run({ http, assert, state }) {
219
219
  if (!state.postUrl) {
@@ -246,7 +246,7 @@ module.exports = {
246
246
  name: 'verify-edit',
247
247
  auth: 'admin',
248
248
  timeout: 30000,
249
- skip: !process.env.GITHUB_TOKEN ? 'GITHUB_TOKEN env var not set' : false,
249
+ skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
250
250
 
251
251
  async run({ assert, state }) {
252
252
  if (!state.postPath || state.editSkipped) {
@@ -254,7 +254,7 @@ module.exports = {
254
254
  return;
255
255
  }
256
256
 
257
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
257
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
258
258
 
259
259
  // Fetch the file directly from GitHub
260
260
  const { data: fileData } = await octokit.rest.repos.getContent({
@@ -311,11 +311,11 @@ module.exports = {
311
311
  timeout: 60000,
312
312
 
313
313
  async run({ state }) {
314
- if (!process.env.GITHUB_TOKEN || !state.postPath) {
314
+ if (!process.env.GH_TOKEN || !state.postPath) {
315
315
  return;
316
316
  }
317
317
 
318
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
318
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
319
319
 
320
320
  // Get the current SHA (may have changed after edit)
321
321
  try {
@@ -4,7 +4,7 @@
4
4
  * Writes arbitrary content to a GitHub repository
5
5
  * Requires admin/blogger role, GitHub API key, and repo_website config
6
6
  *
7
- * IMPORTANT: These tests require GITHUB_TOKEN and github.repo_website to be configured.
7
+ * IMPORTANT: These tests require GH_TOKEN and github.repo_website to be configured.
8
8
  * If GitHub is not configured, the tests will fail.
9
9
  *
10
10
  * This is a suite because we need to clean up created files and cancel workflows after tests.
@@ -155,11 +155,11 @@ module.exports = {
155
155
  timeout: 60000,
156
156
 
157
157
  async run({ state, config }) {
158
- if (!process.env.GITHUB_TOKEN || !config.github?.repo_website) {
158
+ if (!process.env.GH_TOKEN || !config.github?.repo_website) {
159
159
  return;
160
160
  }
161
161
 
162
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
162
+ const octokit = new Octokit({ auth: process.env.GH_TOKEN });
163
163
 
164
164
  // Parse owner/repo from githubRepoWebsite (e.g., 'https://github.com/owner/repo')
165
165
  const repoMatch = config.github?.repo_website.match(/github\.com\/([^/]+)\/([^/]+)/);