11import { exec } from 'child_process'
22import os from 'os'
3- import { lt } from 'semver'
3+ import { gte , lt } from 'semver'
44import { promisify } from 'util'
55
66const doExec = promisify ( exec )
7+ const VERSIONED_ARCHIVE_VERSION = '2.99.0'
8+ const LATEST_RELEASE_URL =
9+ 'https://api.github.com/repos/supabase/cli/releases/latest'
10+
11+ export type ArchiveFormat = 'tar' | 'zip'
12+
13+ export type DownloadArchive = {
14+ url : string
15+ format : ArchiveFormat
16+ }
717
818// arch in [arm, arm64, x64...] (https://nodejs.org/docs/latest-v16.x/api/os.html#osarch)
919// return value in [amd64, arm64, arm]
@@ -23,17 +33,73 @@ const mapOS = (platform: string): string => {
2333 return mappings [ platform ] || platform
2434}
2535
26- export const getDownloadUrl = async ( version : string ) : Promise < string > => {
27- const platform = mapOS ( os . platform ( ) )
28- const arch = mapArch ( os . arch ( ) )
29- const filename = `supabase_${ platform } _${ arch } .tar.gz`
30- if ( version . toLowerCase ( ) === 'latest' ) {
31- return `https://github.com/supabase/cli/releases/latest/download/${ filename } `
36+ const normalizeVersion = ( version : string ) : string => version . replace ( / ^ v / i, '' )
37+
38+ const resolveLatestVersion = async ( ) : Promise < string > => {
39+ const response = await fetch ( LATEST_RELEASE_URL )
40+ if ( ! response . ok ) {
41+ throw new Error (
42+ `Failed to resolve latest Supabase CLI release: ${ response . statusText } `
43+ )
44+ }
45+
46+ const release = ( await response . json ( ) ) as { tag_name ?: unknown }
47+ if ( typeof release . tag_name !== 'string' ) {
48+ throw new Error (
49+ 'Failed to resolve latest Supabase CLI release: missing tag name'
50+ )
51+ }
52+
53+ return normalizeVersion ( release . tag_name )
54+ }
55+
56+ const getArchiveFormat = ( version : string , platform : string ) : ArchiveFormat => {
57+ if ( platform === 'win32' && gte ( version , VERSIONED_ARCHIVE_VERSION ) ) {
58+ return 'zip'
3259 }
60+
61+ return 'tar'
62+ }
63+
64+ const getArchiveFilename = (
65+ version : string ,
66+ platform : string ,
67+ arch : string
68+ ) : string => {
69+ const archivePlatform = mapOS ( platform )
70+ const archiveArch = mapArch ( arch )
3371 if ( lt ( version , '1.28.0' ) ) {
34- return `https://github.com/supabase/cli/releases/download/v${ version } /supabase_${ version } _${ platform } _${ arch } .tar.gz`
72+ return `supabase_${ version } _${ archivePlatform } _${ archiveArch } .tar.gz`
73+ }
74+
75+ if ( gte ( version , VERSIONED_ARCHIVE_VERSION ) ) {
76+ const extension = platform === 'win32' ? 'zip' : 'tar.gz'
77+ return `supabase_${ version } _${ archivePlatform } _${ archiveArch } .${ extension } `
78+ }
79+
80+ return `supabase_${ archivePlatform } _${ archiveArch } .tar.gz`
81+ }
82+
83+ export const getDownloadArchive = async (
84+ version : string ,
85+ platform = os . platform ( ) ,
86+ arch = os . arch ( )
87+ ) : Promise < DownloadArchive > => {
88+ const resolvedVersion =
89+ version . toLowerCase ( ) === 'latest'
90+ ? await resolveLatestVersion ( )
91+ : normalizeVersion ( version )
92+ const filename = getArchiveFilename ( resolvedVersion , platform , arch )
93+
94+ return {
95+ url : `https://github.com/supabase/cli/releases/download/v${ resolvedVersion } /${ filename } ` ,
96+ format : getArchiveFormat ( resolvedVersion , platform )
3597 }
36- return `https://github.com/supabase/cli/releases/download/v${ version } /${ filename } `
98+ }
99+
100+ export const getDownloadUrl = async ( version : string ) : Promise < string > => {
101+ const archive = await getDownloadArchive ( version )
102+ return archive . url
37103}
38104
39105export const determineInstalledVersion = async ( ) : Promise < string > => {
0 commit comments