howcode 0.1.65 → 0.1.67-dev.0

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.
Files changed (2) hide show
  1. package/lib/howcode.js +56 -7
  2. package/package.json +3 -2
package/lib/howcode.js CHANGED
@@ -12,6 +12,13 @@ const packageJson = require('../package.json')
12
12
 
13
13
  const APP_NAME = packageJson.howcode.appName
14
14
  const RELEASE_BASE_URL = process.env.HOWCODE_BASE_URL || packageJson.howcode.releaseBaseUrl
15
+ const RELEASE_CHANNEL =
16
+ process.env.HOWCODE_RELEASE_CHANNEL || packageJson.howcode.releaseChannel || 'main'
17
+ const CHANNEL_RELEASE_TAGS = { main: 'channel-main', dev: 'channel-dev' }
18
+ const trailingSlashesPattern = /\/+$/
19
+ const trailingChannelPattern = /\/(?:main|dev|channel-main|channel-dev)$/i
20
+ const releaseTagPlaceholderPattern = /\{releaseTag\}/g
21
+ const channelPlaceholderPattern = /\{channel\}/g
15
22
  const FETCH_METADATA_TIMEOUT_MS = 30_000
16
23
  const DOWNLOAD_IDLE_TIMEOUT_MS = 60_000
17
24
 
@@ -84,15 +91,35 @@ function getCacheRoot() {
84
91
  return path.join(process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache'), APP_NAME)
85
92
  }
86
93
 
94
+ function getReleaseChannel() {
95
+ if (RELEASE_CHANNEL === 'main' || RELEASE_CHANNEL === 'dev') return RELEASE_CHANNEL
96
+ throw new Error(`Unsupported release channel: ${RELEASE_CHANNEL}`)
97
+ }
98
+
99
+ function getChannelReleaseTag(channel) {
100
+ return CHANNEL_RELEASE_TAGS[channel]
101
+ }
102
+
103
+ function getReleaseBaseUrl(channel = getReleaseChannel()) {
104
+ const releaseTag = getChannelReleaseTag(channel)
105
+ const baseUrl = RELEASE_BASE_URL.replace(trailingSlashesPattern, '')
106
+
107
+ if (baseUrl.includes('{releaseTag}'))
108
+ return baseUrl.replace(releaseTagPlaceholderPattern, releaseTag)
109
+ if (baseUrl.includes('{channel}')) return baseUrl.replace(channelPlaceholderPattern, releaseTag)
110
+
111
+ return baseUrl.replace(trailingChannelPattern, `/${releaseTag}`)
112
+ }
113
+
87
114
  function getPaths(target, releaseInfo) {
88
115
  const cacheRoot = getCacheRoot()
89
116
  const versionsRoot = path.join(cacheRoot, 'versions')
90
- const releaseKey = `${releaseInfo.version}-${releaseInfo.hash}`
117
+ const releaseKey = `${releaseInfo.channel}-${releaseInfo.version}-${releaseInfo.hash}`
91
118
  const installDir = path.join(versionsRoot, releaseKey)
92
119
  const launcherWorkingDirectory = path.dirname(path.join(installDir, target.executable))
93
120
  return {
94
121
  cacheRoot,
95
- currentFile: path.join(cacheRoot, 'current.json'),
122
+ currentFile: path.join(cacheRoot, `current-${releaseInfo.channel}.json`),
96
123
  windowsCommandFile: path.join(cacheRoot, `${APP_NAME}.cmd`),
97
124
  launcherWorkingDirectory,
98
125
  installDir,
@@ -344,16 +371,22 @@ async function sha256File(filePath) {
344
371
  }
345
372
 
346
373
  async function resolveLatestRelease(target) {
347
- const updateUrl = `${RELEASE_BASE_URL}/stable-${target.os}-${target.arch}-update.json`
374
+ const channel = getReleaseChannel()
375
+ const releaseBaseUrl = getReleaseBaseUrl(channel)
376
+ const updateUrl = `${releaseBaseUrl}/stable-${target.os}-${target.arch}-update.json`
348
377
  const metadata = await fetchJson(updateUrl)
349
378
  if (!metadata || typeof metadata.version !== 'string' || typeof metadata.hash !== 'string') {
350
379
  throw new Error(`Invalid release metadata from ${updateUrl}`)
351
380
  }
352
381
 
353
382
  return {
383
+ channel,
354
384
  version: metadata.version,
355
385
  hash: metadata.hash,
356
- assetUrl: `${RELEASE_BASE_URL}/${APP_NAME}-${target.os}-${target.arch}.tar.gz`,
386
+ assetUrl:
387
+ typeof metadata.assetUrl === 'string' && metadata.assetUrl.length > 0
388
+ ? metadata.assetUrl
389
+ : `${releaseBaseUrl}/${APP_NAME}-${target.os}-${target.arch}.tar.gz`,
357
390
  }
358
391
  }
359
392
 
@@ -399,6 +432,7 @@ async function installRelease(target, releaseInfo, paths) {
399
432
  JSON.stringify(
400
433
  {
401
434
  version: releaseInfo.version,
435
+ channel: releaseInfo.channel,
402
436
  hash: releaseInfo.hash,
403
437
  installDir: paths.installDir,
404
438
  executablePath: paths.executablePath,
@@ -409,8 +443,22 @@ async function installRelease(target, releaseInfo, paths) {
409
443
  )
410
444
  }
411
445
 
446
+ async function getPruneKeepDirs(cacheRoot, keepDir) {
447
+ const keepDirs = new Set([keepDir])
448
+
449
+ for (const channel of Object.keys(CHANNEL_RELEASE_TAGS)) {
450
+ const record = readJsonIfPresent(path.join(cacheRoot, `current-${channel}.json`))
451
+ if (record?.installDir) {
452
+ keepDirs.add(record.installDir)
453
+ }
454
+ }
455
+
456
+ return keepDirs
457
+ }
458
+
412
459
  async function pruneOldVersions(cacheRoot, keepDir) {
413
460
  const versionsRoot = path.join(cacheRoot, 'versions')
461
+ const keepDirs = await getPruneKeepDirs(cacheRoot, keepDir)
414
462
  let entries = []
415
463
 
416
464
  try {
@@ -423,7 +471,7 @@ async function pruneOldVersions(cacheRoot, keepDir) {
423
471
  entries
424
472
  .filter((entry) => entry.isDirectory())
425
473
  .map((entry) => path.join(versionsRoot, entry.name))
426
- .filter((dirPath) => dirPath !== keepDir)
474
+ .filter((dirPath) => !keepDirs.has(dirPath))
427
475
  .map((dirPath) => fsp.rm(dirPath, { recursive: true, force: true })),
428
476
  )
429
477
  }
@@ -456,7 +504,8 @@ async function main() {
456
504
  const cacheRoot = getCacheRoot()
457
505
  await fsp.mkdir(cacheRoot, { recursive: true })
458
506
 
459
- const current = readJsonIfPresent(path.join(cacheRoot, 'current.json'))
507
+ const channel = getReleaseChannel()
508
+ const current = readJsonIfPresent(path.join(cacheRoot, `current-${channel}.json`))
460
509
 
461
510
  let releaseInfo = null
462
511
  try {
@@ -465,7 +514,7 @@ async function main() {
465
514
  if (current?.executablePath) {
466
515
  const currentPaths = {
467
516
  cacheRoot,
468
- currentFile: path.join(cacheRoot, 'current.json'),
517
+ currentFile: path.join(cacheRoot, `current-${channel}.json`),
469
518
  windowsCommandFile: path.join(cacheRoot, `${APP_NAME}.cmd`),
470
519
  installDir: current.installDir || path.dirname(path.dirname(current.executablePath)),
471
520
  launcherWorkingDirectory: path.dirname(current.executablePath),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "howcode",
3
- "version": "0.1.65",
3
+ "version": "0.1.67-dev.0",
4
4
  "description": "Desktop coding app for Pi with projects, terminal, git, and diff workflows.",
5
5
  "license": "MIT",
6
6
  "author": "Igor Warzocha",
@@ -38,6 +38,7 @@
38
38
  ],
39
39
  "howcode": {
40
40
  "appName": "howcode",
41
- "releaseBaseUrl": "https://github.com/IgorWarzocha/howcode/releases/download/main"
41
+ "releaseChannel": "dev",
42
+ "releaseBaseUrl": "https://github.com/IgorWarzocha/howcode/releases/download/channel-dev"
42
43
  }
43
44
  }