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

fix: updates to typescript w/n the env command #6905

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
213 changes: 213 additions & 0 deletions src/commands/api-types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import type { NetlifyAPI } from 'netlify'

import { DeployContext } from './types.d.ts'

type ApiContext = DeployContext | 'branch'

interface UpdatedBy {
id: string;
full_name: string;
email: string;
avatar_url: string;
}

export type NarrowedEnvVarValue = Pick<EnvVarValue, 'value' | 'context' | 'context_parameter'>

interface EnvVarValue {
value: string,
context: ApiContext,
context_parameter?: string,
id?: string,
}

export interface EnvVar {
key: string;
scopes: Scope[];
values: EnvVarValue[];
is_secret?: boolean;
updated_at?: string;
updated_by?: UpdatedBy;
}

interface GetEnvParams {
accountId: string,
siteId?: string,
context?: DeployContext,
scope?: EnvironmentVariableScope
}

interface DeleteEnvVarValueParams {
accountId: string,
key: string,
id?: string,
siteId?: string
}

interface SetEnvVarValueBody {
context: string,
value: string,
contextParameter?: string,
}

interface SetEnvVarValueParams {
accountId: string,
key: string,
siteId?: string,
body: SetEnvVarValueBody
}

interface UpdateEnvVarBody {
key: string,
scopes: string[],
values: EnvVar[]
is_secret: boolean
}

interface UpdateEnvVarParams {
accountId: string,
key: string,
siteId?: string
body: EnvVar
}

interface CreateEnvVarParams {
accountId: string,
key?: string,
siteId?: string,
body: EnvVar[]
}

interface SiteInfo {
id: string;
state: string;
plan: string;
name: string;
custom_domain: string | null;
domain_aliases: string[];
branch_deploy_custom_domain: string | null;
deploy_preview_custom_domain: string | null;
password: string | null;
notification_email: string | null;
url: string;
ssl_url: string;
admin_url: string;
screenshot_url: string;
created_at: string;
updated_at: string;
user_id: string;
session_id: string;
ssl: boolean;
force_ssl: boolean | null;
managed_dns: boolean;
deploy_url: string;
published_deploy: PublishedDeploy;
account_id: string;
account_name: string;
account_slug: string;
git_provider?: string;
deploy_hook: string;
capabilities: Capabilities;
processing_settings: ProcessingSettings;
build_settings: BuildSettings;
id_domain: string;
default_hooks_data?: DefaultHooksData;
build_image: string;
prerender: string | null;
functions_region: string;
feature_flags: FeatureFlags;
}

interface PublishedDeploy {
id: string;
site_id: string;
user_id: string;
build_id: string;
state: string;
name: string;
url: string;
ssl_url: string;
admin_url: string;
deploy_url: string;
deploy_ssl_url: string;
screenshot_url: string;
review_id: number | null;
draft: boolean;
required: string[];
required_functions: string[];
error_message: string;
branch: string;
commit_ref: string;
commit_url: string;
skipped: boolean | null;
created_at: string;
updated_at: string;
published_at: string;
title: string;
context: string;
locked: boolean | null;
review_url: string | null;
framework: string;
function_schedules: FunctionSchedule[] | [];
}

interface FunctionSchedule {
name: string;
cron: string;
}

interface Capabilities {
[key: string]: Record<string, unknown>;
}

// Processing Settings Interface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need all these comments, they're all basically just saying the name of the interfaces are

interface ProcessingSettings {
html: HTMLProcessingSettings;
}

// HTML Processing Settings Interface
interface HTMLProcessingSettings {
pretty_urls: boolean;
}

interface BuildSettings {
id: number;
provider: string;
deploy_key_id: string;
repo_path: string;
repo_branch: string;
dir: string;
functions_dir: string;
cmd: string;
allowed_branches: string[];
public_repo: boolean;
private_logs: boolean;
repo_url: string;
env: EnvVariables;
installation_id: number;
stop_builds: boolean;
}

interface EnvVariables {
[key: string]: string;
}

interface DefaultHooksData {
access_token: string;
}

interface GetSiteParams {
siteId?: string,
feature_flags?: string
site_id?: string
}

export interface ExtendedNetlifyAPI extends NetlifyAPI {
getEnvVar(params: GetEnvVarParams): Promise<EnvVar>
getEnvVars( params: GetEnvParams): Promise<EnvVar[]>
deleteEnvVarValue( params: DeleteEnvVarValueParams ): Promise<void>
setEnvVarValue( params: SetEnvVarValueParams): Promise<EnvVar>
deleteEnvVar(params: DeleteEnvVarValueParams): Promise<void>
updateEnvVar(params: UpdateEnvVarParams): Promise<EnvVar>
createEnvVars(params: CreateEnvVarParams): Promise<EnvVar[]>
getSite(params: GetSiteParams): Promise<SiteInfo>
}
4 changes: 3 additions & 1 deletion src/commands/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export const apiCommand = async (apiMethod: string, options: OptionValues, comma
const apiResponse = await api[apiMethod](payload)
logJson(apiResponse)
} catch (error_) {
error(error_)
if (error_ instanceof Error || typeof error_ === 'string') {
error(error_)
}
}
}
3 changes: 1 addition & 2 deletions src/commands/base-command.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { isCI } from 'ci-info'

import { existsSync } from 'fs'
import { join, relative, resolve } from 'path'
import process from 'process'
Expand All @@ -8,6 +6,7 @@ import { format } from 'util'
import { DefaultLogger, Project } from '@netlify/build-info'
import { NodeFS, NoopLogger } from '@netlify/build-info/node'
import { resolveConfig } from '@netlify/config'
import { isCI } from 'ci-info'
import { Command, Help, Option } from 'commander'
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'debu... Remove this comment to see the full error message
import debug from 'debug'
Expand Down
43 changes: 24 additions & 19 deletions src/commands/env/env-clone.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { OptionValues } from 'commander'

import { chalk, log, error as logError } from '../../utils/command-helpers.js'
import { isAPIEnvError } from '../../utils/env/index.js'
import type { ExtendedNetlifyAPI } from '../api-types.d.ts'
import BaseCommand from '../base-command.js'
import { $TSFixMe } from '../types.js'

import { CloneEnvParams, EnvCloneOptions } from './types.js'

// @ts-expect-error TS(7006) FIXME: Parameter 'api' implicitly has an 'any' type.
const safeGetSite = async (api, siteId) => {
const safeGetSite = async (api: ExtendedNetlifyAPI, siteId: string) => {
try {
const data = await api.getSite({ siteId })
return { data }
Expand All @@ -17,14 +19,12 @@ const safeGetSite = async (api, siteId) => {
* Copies the env from a site configured with Envelope to a different site configured with Envelope
* @returns {Promise<boolean>}
*/
// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message
const cloneEnvVars = async ({ api, siteFrom, siteTo }): Promise<boolean> => {
const cloneEnvVars = async ({ api, siteFrom, siteTo }: CloneEnvParams): Promise<boolean> => {
const [envelopeFrom, envelopeTo] = await Promise.all([
api.getEnvVars({ accountId: siteFrom.account_slug, siteId: siteFrom.id }),
api.getEnvVars({ accountId: siteTo.account_slug, siteId: siteTo.id }),
])

// @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message
const keysFrom = envelopeFrom.map(({ key }) => key)

if (keysFrom.length === 0) {
Expand All @@ -34,24 +34,21 @@ const cloneEnvVars = async ({ api, siteFrom, siteTo }): Promise<boolean> => {

const accountId = siteTo.account_slug
const siteId = siteTo.id
// @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message
const envVarsToDelete = envelopeTo.filter(({ key }) => keysFrom.includes(key))
// delete marked env vars in parallel
// @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message
await Promise.all(envVarsToDelete.map(({ key }) => api.deleteEnvVar({ accountId, siteId, key })))

// hit create endpoint
try {
await api.createEnvVars({ accountId, siteId, body: envelopeFrom })
} catch (error) {
// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
throw error.json ? error.json.msg : error
} catch (error: unknown) {
if (isAPIEnvError(error)) throw error.json ? error.json.msg : error
}

return true
}

export const envClone = async (options: OptionValues, command: BaseCommand) => {
export const envClone = async (options: EnvCloneOptions, command: BaseCommand) => {
const { api, site } = command.netlify

if (!site.id && !options.from) {
Expand All @@ -61,8 +58,14 @@ export const envClone = async (options: OptionValues, command: BaseCommand) => {
return false
}

const sourceId = options.from || site.id

if (!sourceId) {
throw new Error('Site ID is required')
}

const siteId = {
from: options.from || site.id,
from: sourceId,
to: options.to,
}

Expand All @@ -81,13 +84,15 @@ export const envClone = async (options: OptionValues, command: BaseCommand) => {
return false
}

const success = await cloneEnvVars({ api, siteFrom, siteTo })
if (siteFrom && siteTo) {
const success = await cloneEnvVars({ api, siteFrom, siteTo })

if (!success) {
return false
}
if (!success) {
return false
}

log(`Successfully cloned environment variables from ${chalk.green(siteFrom.name)} to ${chalk.green(siteTo.name)}`)
log(`Successfully cloned environment variables from ${chalk.green(siteFrom.name)} to ${chalk.green(siteTo.name)}`)
}

return true
}
9 changes: 5 additions & 4 deletions src/commands/env/env-get.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { OptionValues } from 'commander'

import { chalk, log, logJson } from '../../utils/command-helpers.js'
import { AVAILABLE_CONTEXTS, getEnvelopeEnv } from '../../utils/env/index.js'
import BaseCommand from '../base-command.js'

export const envGet = async (name: string, options: OptionValues, command: BaseCommand) => {
import { EnvOptions } from './types.js'

export const envGet = async (name: string, options: EnvOptions, command: BaseCommand) => {
const { context, scope } = options
const { api, cachedConfig, site } = command.netlify
const siteId = site.id
Expand All @@ -15,6 +15,7 @@ export const envGet = async (name: string, options: OptionValues, command: BaseC
}

const { siteInfo } = cachedConfig

const env = await getEnvelopeEnv({ api, context, env: cachedConfig.env, key: name, scope, siteInfo })

const { value } = env[name] || {}
Expand All @@ -26,7 +27,7 @@ export const envGet = async (name: string, options: OptionValues, command: BaseC
}

if (!value) {
const contextType = AVAILABLE_CONTEXTS.includes(context) ? 'context' : 'branch'
const contextType = context === undefined ? 'branch' : AVAILABLE_CONTEXTS.includes(context)
const withContext = `in the ${chalk.magenta(context)} ${contextType}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this changed? It now returns a boolean instead of a string

const withScope = scope === 'any' ? '' : ` and the ${chalk.magenta(scope)} scope`
log(`No value set ${withContext}${withScope} for environment variable ${chalk.yellow(name)}`)
Expand Down
Loading
Loading