Skip to content

Commit

Permalink
feat: Update create command to generate .env and token (#209)
Browse files Browse the repository at this point in the history
  • Loading branch information
HugoRCD authored Sep 20, 2024
1 parent 4460883 commit 512ee53
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 13 deletions.
5 changes: 3 additions & 2 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

[![npm version](https://img.shields.io/npm/v/@shelve/cli?color=black)](https://npmjs.com/package/@shelve/cli)
[![npm downloads](https://img.shields.io/npm/dm/@shelve/cli?color=black)](https://npmjs.com/package/@shelve/cli)
[![license](https://img.shields.io/github/license/HugoRCD/shelve?color=black)](https://github.com/HugoRCD/shelve/blob/main/LICENSE)

<!-- /automd -->

Expand All @@ -19,7 +20,7 @@ npm install -g @shelve/cli

## Configuration

Configuration is loaded by [unjs/c12](https://github.com/unjs/c12) from cwd. You can use either `shelve.config.json`, `shelve.config.{ts,js,mjs,cjs}` or use the `shelve` field in `package.json`.
Configuration is loaded by [unjs/c12](https://github.com/unjs/c12) from cwd. You can use either `shelve.config.json`, `shelve.config.{ts,js,mjs,cjs}`, but running the CLI without any configuration will create a `shelve.config.json` file.
You have the option to create a `shelve.config.ts` file to enable type checking and autocompletion. The file should contain the following content:

```ts title="shelve.config.ts"
Expand Down Expand Up @@ -107,6 +108,6 @@ Made by [@HugoRCD](https://github.com/HugoRCD) and [community](https://github.co

---

_🤖 auto updated with [automd](https://automd.unjs.io) (last updated: Fri Sep 13 2024)_
_🤖 auto updated with [automd](https://automd.unjs.io) (last updated: Fri Sep 20 2024)_

<!-- /automd -->
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@shelve/cli",
"version": "2.2.0",
"version": "2.2.1",
"description": "The command-line interface for Shelve",
"homepage": "https://shelve.hrcd.fr",
"bugs": {
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/commands/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export function pullCommand(program: Command): void {

const projectData = await getProjectByName(project)
const variables = await getEnvVariables(projectData.id, environment)
await createEnvFile(pullMethod, envFileName, variables)
await createEnvFile({ method: pullMethod, envFileName, variables })
outro(`Successfully pulled variable from ${environment} environment`)
process.exit(0)
})
}
3 changes: 2 additions & 1 deletion packages/cli/src/commands/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export function pushCommand(program: Command): void {

const projectData = await getProjectByName(project)
const variables = await getEnvFile()
await pushEnvFile(variables, projectData.id, environment)
await pushEnvFile({ variables, projectId: projectData.id, environment })
outro(`Successfully pushed variable to ${environment} environment`)
process.exit(0)
})
}
22 changes: 21 additions & 1 deletion packages/cli/src/utils/api.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import { ofetch } from 'ofetch'
import { isCancel, password } from '@clack/prompts'
import { getConfig } from './config'
import { mergeEnvFile } from './env'
import { onCancel } from './index'

async function getToken(): Promise<string> {
const token = await password({
message: 'Please provide a valid token (you can generate one on https://shelve.hrcd.fr/app/tokens)',
validate(value) {
if (value.length === 0) return `Value is required!`
},
})

if (isCancel(token)) onCancel('Operation cancelled.')

await mergeEnvFile([{ key: 'SHELVE_TOKEN', value: token }])

return token
}

export async function useApi(): Promise<typeof ofetch> {
const { config } = await getConfig()
const { token, url } = config
const { url } = config
let { token } = config

if (!token) token = await getToken()

const sanitizedUrl = url.replace(/\/+$/, '') // remove trailing slash
const baseURL = `${sanitizedUrl}/api`
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { loadConfig, setupDotenv, type ConfigLayer } from 'c12'
import consola from 'consola'
import { SHELVE_JSON_SCHEMA, ShelveConfig } from '@shelve/types'
import { getProjects } from './project'
import { getKeyValue } from './env'
import { onCancel } from './index'

export async function createShelveConfig(projectName?: string): Promise<string> {
Expand Down Expand Up @@ -89,6 +90,9 @@ export async function loadShelveConfig(): Promise<ShelveConfig> {
process.exit(0)
}

const envToken = await getKeyValue('SHELVE_TOKEN')
if (envToken) config.token = envToken

if (!config.token) {
consola.error('You need to provide a token')
process.exit(0)
Expand Down
30 changes: 23 additions & 7 deletions packages/cli/src/utils/env.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs'
import type { Env, Environment, VariablesCreateInput } from '@shelve/types'
import type { CreateEnvFileInput, Env, PushEnvFileInput, VariablesCreateInput } from '@shelve/types'
import { spinner } from '@clack/prompts'
import { loadShelveConfig } from './config'
import { getConfig, loadShelveConfig } from './config'
import { useApi } from './api'
import { onCancel } from './index'

Expand All @@ -11,8 +11,20 @@ export function isEnvFileExist(envFileName: string): boolean {
return fs.existsSync(envFileName)
}

export async function getKeyValue(key: string): Promise<string> {
const { config } = await getConfig()
const { envFileName } = config
const envFile = await getEnvFile()
const value = envFile.find((item) => item.key === key)?.value
if (!value) {
onCancel(`Key ${key} not found in ${envFileName}`)
}
return value
}

export async function mergeEnvFile(variables: Env[] = []): Promise<void> {
const { envFileName } = await loadShelveConfig()
const { config } = await getConfig()
const { envFileName } = config
s.start(`Merging ${envFileName} file`)
const envFile = await getEnvFile()
envFile.push(...variables)
Expand All @@ -22,10 +34,11 @@ export async function mergeEnvFile(variables: Env[] = []): Promise<void> {
s.stop(`Merging ${envFileName} file`)
}

export async function createEnvFile(pullMethod: string, envFileName: string, variables: Env[] = []): Promise<void> {
export async function createEnvFile(input: CreateEnvFileInput): Promise<void> {
const { method, envFileName, variables } = input
const envFileExist = isEnvFileExist(envFileName)
try {
if (envFileExist && pullMethod === 'merge') {
if (envFileExist && method === 'merge') {
await mergeEnvFile(variables)
return
}
Expand All @@ -41,11 +54,13 @@ export async function createEnvFile(pullMethod: string, envFileName: string, var
}

export async function getEnvFile(): Promise<Env[]> {
const { envFileName } = await loadShelveConfig()
const { config } = await getConfig()
const { envFileName } = config
const isExist = fs.existsSync(envFileName)
if (isExist) {
const envFile = fs.readFileSync(envFileName, 'utf8')
const envFileContent = envFile.split('\n').filter((item) => item && !item.startsWith('#')).join('\n')
if (!envFileContent) return []
return envFileContent.split('\n').map((item) => {
const [key, value] = item.split('=')
if (!key || !value) {
Expand All @@ -70,7 +85,8 @@ export async function getEnvVariables(projectId: number, environment: string): P
}
}

export async function pushEnvFile(variables: Env[], projectId: number, environment: Environment): Promise<void> {
export async function pushEnvFile(input: PushEnvFileInput): Promise<void> {
const { variables, projectId, environment } = input
const api = await useApi()

s.start('Pushing variables')
Expand Down
38 changes: 38 additions & 0 deletions packages/types/src/Cli.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Env, Environment } from './Variables'

export const SHELVE_JSON_SCHEMA = 'https://raw.githubusercontent.com/HugoRCD/shelve/main/packages/types/shelveConfigSchema.json'

export type ShelveConfig = {
Expand Down Expand Up @@ -47,3 +49,39 @@ export type ShelveConfig = {
* */
envFileName: string
}

export type CreateEnvFileInput = {
/**
* The method to use for .env file (overwrite or append)
* Overwrite will replace all existing variables in Shelve app with the ones in the .env file
* Merge will append the .env file to the existing variables in Shelve app
*
* @default 'overwrite'
* */
method: 'overwrite' | 'merge'
/**
* Name of your env file
*
* @default '.env'
* */
envFileName: string
/**
* The variables to create in the .env file
* */
variables: Env[]
}

export type PushEnvFileInput = {
/**
* The variables to push to Shelve
* */
variables: Env[]
/**
* The project ID
* */
projectId: number
/**
* The environment to push the variables to
* */
environment: Environment
}

0 comments on commit 512ee53

Please sign in to comment.