Skip to content

Commit 323613d

Browse files
author
Caleb Barnes
committed
fix awaiting drizzle deps installing / add status check and getSiteConfiguration
1 parent e24d7dc commit 323613d

File tree

3 files changed

+160
-30
lines changed

3 files changed

+160
-30
lines changed

src/commands/database/database.ts

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { OptionValues } from 'commander'
22
import inquirer from 'inquirer'
33
import BaseCommand from '../base-command.js'
4-
import { getExtension, getExtensionInstallations, installExtension } from './utils.js'
4+
import { getExtension, getExtensionInstallations, getSiteConfiguration, installExtension } from './utils.js'
55
import { initDrizzle } from './drizzle.js'
66

77
const NETLIFY_DATABASE_EXTENSION_SLUG = '-94w9m6w-netlify-database-extension'
@@ -37,7 +37,8 @@ const init = async (_options: OptionValues, command: BaseCommand) => {
3737
if (!command.netlify.api.accessToken) {
3838
throw new Error(`No access token found, please login with netlify login`)
3939
}
40-
40+
console.log(`Initializing a new database for site: ${command.siteId}
41+
Please wait...`)
4142
let site: Awaited<ReturnType<typeof command.netlify.api.getSite>>
4243
try {
4344
// @ts-expect-error -- feature_flags is not in the types
@@ -76,16 +77,27 @@ const init = async (_options: OptionValues, command: BaseCommand) => {
7677
if (!dbExtensionInstallation) {
7778
console.log(`Netlify Database extension not installed on team ${site.account_id}, attempting to install now...`)
7879

79-
const installed = await installExtension({
80-
accountId: site.account_id,
81-
token: netlifyToken,
82-
slug: NETLIFY_DATABASE_EXTENSION_SLUG,
83-
hostSiteUrl: extension.hostSiteUrl ?? '',
84-
})
85-
if (!installed) {
86-
throw new Error(`Failed to install extension on team ${site.account_id}: ${NETLIFY_DATABASE_EXTENSION_SLUG}`)
80+
const answers = await inquirer.prompt([
81+
{
82+
type: 'confirm',
83+
name: 'installExtension',
84+
message: `Netlify Database extension is not installed on team ${site.account_id}, would you like to install it now?`,
85+
},
86+
])
87+
if (answers.installExtension) {
88+
const installed = await installExtension({
89+
accountId: site.account_id,
90+
token: netlifyToken,
91+
slug: NETLIFY_DATABASE_EXTENSION_SLUG,
92+
hostSiteUrl: extension.hostSiteUrl ?? '',
93+
})
94+
if (!installed) {
95+
throw new Error(`Failed to install extension on team ${site.account_id}: ${NETLIFY_DATABASE_EXTENSION_SLUG}`)
96+
}
97+
console.log(`Netlify Database extension installed on team ${site.account_id}`)
98+
} else {
99+
return
87100
}
88-
console.log(`Netlify Database extension installed on team ${site.account_id}`)
89101
}
90102

91103
try {
@@ -125,6 +137,60 @@ const init = async (_options: OptionValues, command: BaseCommand) => {
125137
return
126138
}
127139

140+
const status = async (_options: OptionValues, command: BaseCommand) => {
141+
if (!command.siteId) {
142+
console.error(`The project must be linked with netlify link before initializing a database.`)
143+
return
144+
}
145+
// check if this site has a db initialized
146+
const site = await command.netlify.api.getSite({ siteId: command.siteId })
147+
if (!site.account_id) {
148+
throw new Error(`No account id found for site ${command.siteId}`)
149+
}
150+
if (!command.netlify.api.accessToken) {
151+
throw new Error(`You must be logged in with netlify login to check the status of the database`)
152+
}
153+
const netlifyToken = command.netlify.api.accessToken.replace('Bearer ', '')
154+
const extensionInstallation = await getExtensionInstallations({
155+
accountId: site.account_id,
156+
siteId: command.siteId,
157+
token: netlifyToken,
158+
})
159+
160+
if (!extensionInstallation) {
161+
console.log(`Netlify Database extension not installed on team ${site.account_id}`)
162+
return
163+
}
164+
165+
const siteConfig = await getSiteConfiguration({
166+
siteId: command.siteId,
167+
accountId: site.account_id,
168+
slug: NETLIFY_DATABASE_EXTENSION_SLUG,
169+
token: netlifyToken,
170+
})
171+
172+
if (!siteConfig) {
173+
throw new Error(`Failed to get site configuration for site ${command.siteId}`)
174+
}
175+
try {
176+
const siteEnv = await command.netlify.api.getEnvVar({
177+
accountId: site.account_id,
178+
siteId: command.siteId,
179+
key: 'NETLIFY_DATABASE_URL',
180+
})
181+
182+
if (siteEnv.key === 'NETLIFY_DATABASE_URL') {
183+
console.log(`Database initialized for site: ${command.siteId}`)
184+
return
185+
}
186+
} catch {
187+
throw new Error(
188+
`Database not initialized for site: ${command.siteId}.
189+
Run 'netlify db init' to initialize a database`,
190+
)
191+
}
192+
}
193+
128194
export const createDatabaseCommand = (program: BaseCommand) => {
129195
const dbCommand = program.command('db').alias('database').description(`TODO: write description for database command`)
130196

@@ -134,5 +200,7 @@ export const createDatabaseCommand = (program: BaseCommand) => {
134200
.option('--drizzle', 'Sets up drizzle-kit and drizzle-orm in your project')
135201
.action(init)
136202

203+
dbCommand.command('status').description('Check the status of the database').action(status)
204+
137205
return dbCommand
138206
}

src/commands/database/drizzle.ts

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { spawn } from 'child_process'
22
import { carefullyWriteFile } from './utils.js'
33
import BaseCommand from '../base-command.js'
44
import path from 'path'
5-
import fs from 'fs'
5+
import fs from 'fs/promises'
6+
import inquirer from 'inquirer'
67

78
export const initDrizzle = async (command: BaseCommand) => {
89
if (!command.project.root) {
@@ -11,31 +12,43 @@ export const initDrizzle = async (command: BaseCommand) => {
1112
const drizzleConfigFilePath = path.resolve(command.project.root, 'drizzle.config.ts')
1213
await carefullyWriteFile(drizzleConfigFilePath, drizzleConfig)
1314

14-
fs.mkdirSync(path.resolve(command.project.root, 'db'), { recursive: true })
15+
await fs.mkdir(path.resolve(command.project.root, 'db'), { recursive: true })
1516
const schemaFilePath = path.resolve(command.project.root, 'db/schema.ts')
1617
await carefullyWriteFile(schemaFilePath, exampleDrizzleSchema)
1718

1819
const dbIndexFilePath = path.resolve(command.project.root, 'db/index.ts')
1920
await carefullyWriteFile(dbIndexFilePath, exampleDbIndex)
2021

21-
console.log('Adding drizzle-kit and drizzle-orm to the project')
22-
// install dev deps
23-
const devDepProc = spawn(
24-
command.project.packageManager?.installCommand ?? 'npm install',
25-
['drizzle-kit@latest', '-D'],
22+
const packageJsonPath = path.resolve(command.project.root, 'package.json')
23+
24+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
25+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'))
26+
27+
const answers = await inquirer.prompt([
2628
{
27-
stdio: 'inherit',
28-
shell: true,
29+
type: 'confirm',
30+
name: 'updatePackageJson',
31+
message: `Add drizzle db commands to package.json?`,
2932
},
30-
)
31-
devDepProc.on('exit', (code) => {
32-
if (code === 0) {
33-
// install deps
34-
spawn(command.project.packageManager?.installCommand ?? 'npm install', ['drizzle-orm@latest'], {
35-
stdio: 'inherit',
36-
shell: true,
37-
})
33+
])
34+
if (answers.updatePackageJson) {
35+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
36+
packageJson.scripts = {
37+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
38+
...(packageJson.scripts ?? {}),
39+
...packageJsonScripts,
3840
}
41+
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2))
42+
}
43+
44+
await spawnAsync(command.project.packageManager?.installCommand ?? 'npm install', ['drizzle-kit@latest', '-D'], {
45+
stdio: 'inherit',
46+
shell: true,
47+
})
48+
49+
await spawnAsync(command.project.packageManager?.installCommand ?? 'npm install', ['drizzle-orm@latest'], {
50+
stdio: 'inherit',
51+
shell: true,
3952
})
4053
}
4154

@@ -66,3 +79,23 @@ export const db = drizzle({
6679
schema
6780
});
6881
`
82+
83+
const packageJsonScripts = {
84+
'db:generate': 'netlify dev:exec --context dev drizzle-kit generate',
85+
'db:migrate': 'netlify dev:exec --context dev drizzle-kit migrate',
86+
'db:studio': 'netlify dev:exec --context dev drizzle-kit studio',
87+
}
88+
89+
const spawnAsync = (command: string, args: string[], options: Parameters<typeof spawn>[2]): Promise<number> => {
90+
return new Promise((resolve, reject) => {
91+
const child = spawn(command, args, options)
92+
child.on('error', reject)
93+
child.on('exit', (code) => {
94+
if (code === 0) {
95+
resolve(code)
96+
}
97+
const errorMessage = code ? `Process exited with code ${code.toString()}` : 'Process exited with no code'
98+
reject(new Error(errorMessage))
99+
})
100+
})
101+
}

src/commands/database/utils.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fsPromises from 'fs/promises'
12
import fs from 'fs'
23
import inquirer from 'inquirer'
34
import path from 'path'
@@ -96,9 +97,37 @@ export const carefullyWriteFile = async (filePath: string, data: string) => {
9697
},
9798
])
9899
if (answers.overwrite) {
99-
fs.writeFileSync(filePath, data)
100+
await fsPromises.writeFile(filePath, data)
100101
}
101102
} else {
102-
fs.writeFileSync(filePath, data)
103+
await fsPromises.writeFile(filePath, data)
103104
}
104105
}
106+
107+
export const getSiteConfiguration = async ({
108+
siteId,
109+
accountId,
110+
token,
111+
slug,
112+
}: {
113+
siteId: string
114+
accountId: string
115+
token: string
116+
slug: string
117+
}) => {
118+
const siteConfigurationResponse = await fetch(
119+
`${JIGSAW_URL}/team/${accountId}/integrations/${slug}/configuration/site/${siteId}`,
120+
{
121+
headers: {
122+
'netlify-token': token,
123+
},
124+
},
125+
)
126+
127+
if (!siteConfigurationResponse.ok) {
128+
throw new Error(`Failed to fetch extension site configuration for ${siteId}. Is the extension installed?`)
129+
}
130+
131+
const siteConfiguration = await siteConfigurationResponse.json()
132+
return siteConfiguration
133+
}

0 commit comments

Comments
 (0)