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: Update create command to generate .env and token #209

Merged
merged 2 commits into from
Sep 20, 2024
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
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
}