Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): remote template bootstrapper to support write token #8277

Merged
merged 2 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {type CliCommandContext} from '../../types'
import {getDefaultPortForFramework} from '../../util/frameworkPort'
import {
applyEnvVariables,
checkNeedsReadToken,
checkIfNeedsApiToken,
downloadAndExtractRepo,
generateSanityApiReadToken,
generateSanityApiToken,
getGitHubRawContentUrl,
type RepoInfo,
setCorsOrigin,
Expand All @@ -32,6 +32,7 @@ export interface BootstrapRemoteOptions {

const SANITY_DEFAULT_PORT = 3333
const READ_TOKEN_LABEL = 'Live Preview API'
const WRITE_TOKEN_LABEL = 'App Write Token'
const INITIAL_COMMIT_MESSAGE = 'Initial commit from Sanity CLI'

export async function bootstrapRemoteTemplate(
Expand Down Expand Up @@ -65,12 +66,18 @@ export async function bootstrapRemoteTemplate(

debug('Checking if template needs read token')
const needsReadToken = await Promise.all(
(packages ?? ['']).map((pkg) => checkNeedsReadToken(join(outputPath, pkg))),
(packages ?? ['']).map((pkg) => checkIfNeedsApiToken(join(outputPath, pkg), 'read')),
).then((results) => results.some(Boolean))
const needsWriteToken = await Promise.all(
(packages ?? ['']).map((pkg) => checkIfNeedsApiToken(join(outputPath, pkg), 'write')),
).then((results) => results.some(Boolean))

debug('Applying environment variables')
const readToken = needsReadToken
? await generateSanityApiReadToken(READ_TOKEN_LABEL, variables.projectId, apiClient)
? await generateSanityApiToken(READ_TOKEN_LABEL, 'read', variables.projectId, apiClient)
: undefined
const writeToken = needsWriteToken
? await generateSanityApiToken(WRITE_TOKEN_LABEL, 'write', variables.projectId, apiClient)
: undefined

for (const pkg of packages ?? ['']) {
Expand Down Expand Up @@ -106,7 +113,6 @@ export async function bootstrapRemoteTemplate(
if (corsAdded.length) {
output.success(`CORS origins added (${corsAdded.map((p) => `localhost:${p}`).join(', ')})`)
}
if (readToken) {
output.success(`API token generated (${READ_TOKEN_LABEL})`)
}
if (readToken) output.success(`API token generated (${READ_TOKEN_LABEL})`)
if (writeToken) output.success(`API token generated (${WRITE_TOKEN_LABEL})`)
}
17 changes: 11 additions & 6 deletions packages/@sanity/cli/src/util/remoteTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ const DISALLOWED_PATHS = [
const ENV_VAR = {
...REQUIRED_ENV_VAR,
READ_TOKEN: 'SANITY_API_READ_TOKEN',
WRITE_TOKEN: 'SANITY_API_WRITE_TOKEN',
} as const

const API_READ_TOKEN_ROLE = 'viewer'
const API_WRITE_TOKEN_ROLE = 'editor'

type EnvData = {
projectId: string
dataset: string
readToken?: string
writeToken?: string
}

type GithubUrlString =
Expand Down Expand Up @@ -191,17 +196,16 @@ export async function downloadAndExtractRepo(
)
}

export async function checkNeedsReadToken(root: string): Promise<boolean> {
export async function checkIfNeedsApiToken(root: string, type: 'read' | 'write'): Promise<boolean> {
try {
const templatePath = await Promise.any(
ENV_TEMPLATE_FILES.map(async (file) => {
await access(join(root, file))
return file
}),
)

const templateContent = await readFile(join(root, templatePath), 'utf8')
return templateContent.includes(ENV_VAR.READ_TOKEN)
return templateContent.includes(type === 'read' ? ENV_VAR.READ_TOKEN : ENV_VAR.WRITE_TOKEN)
} catch {
return false
}
Expand Down Expand Up @@ -278,8 +282,9 @@ export async function tryApplyPackageName(root: string, name: string): Promise<v
}
}

export async function generateSanityApiReadToken(
export async function generateSanityApiToken(
label: string,
type: 'read' | 'write',
projectId: string,
apiClient: CliApiClient,
): Promise<string> {
Expand All @@ -289,8 +294,8 @@ export async function generateSanityApiReadToken(
uri: `/projects/${projectId}/tokens`,
method: 'POST',
body: {
label: `${label} (${Date.now()})`, // Add timestamp to ensure uniqueness
roleName: 'viewer',
label: `${label} (${Date.now()})`,
roleName: type === 'read' ? API_READ_TOKEN_ROLE : API_WRITE_TOKEN_ROLE,
},
})
return response.key
Expand Down
Loading