From 7eb811d10d4a8085cc16c2bca829a0e22c593123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 12:11:22 +0200 Subject: [PATCH 01/20] AP-3965 Script utility for fetching variables from vault. --- packages/app/script-utils/.eslintignore | 2 + packages/app/script-utils/.eslintrc.json | 11 ++ packages/app/script-utils/.gitignore | 4 + packages/app/script-utils/.prettierignore | 2 + packages/app/script-utils/LICENSE.md | 13 +++ packages/app/script-utils/README.md | 28 +++++ packages/app/script-utils/package.json | 51 +++++++++ packages/app/script-utils/src/index.ts | 1 + packages/app/script-utils/src/vault/dir.ts | 7 ++ .../src/vault/syncEnvWithVault.spec.ts | 64 +++++++++++ .../src/vault/syncEnvWithVault.ts | 92 +++++++++++++++ packages/app/script-utils/src/vault/vault.ts | 106 ++++++++++++++++++ packages/app/script-utils/tsconfig.json | 27 +++++ packages/app/script-utils/tsconfig.lint.json | 5 + packages/app/script-utils/vite.config.ts | 11 ++ 15 files changed, 424 insertions(+) create mode 100644 packages/app/script-utils/.eslintignore create mode 100644 packages/app/script-utils/.eslintrc.json create mode 100644 packages/app/script-utils/.gitignore create mode 100644 packages/app/script-utils/.prettierignore create mode 100644 packages/app/script-utils/LICENSE.md create mode 100644 packages/app/script-utils/README.md create mode 100644 packages/app/script-utils/package.json create mode 100644 packages/app/script-utils/src/index.ts create mode 100644 packages/app/script-utils/src/vault/dir.ts create mode 100644 packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts create mode 100644 packages/app/script-utils/src/vault/syncEnvWithVault.ts create mode 100644 packages/app/script-utils/src/vault/vault.ts create mode 100644 packages/app/script-utils/tsconfig.json create mode 100644 packages/app/script-utils/tsconfig.lint.json create mode 100644 packages/app/script-utils/vite.config.ts diff --git a/packages/app/script-utils/.eslintignore b/packages/app/script-utils/.eslintignore new file mode 100644 index 00000000..b9470778 --- /dev/null +++ b/packages/app/script-utils/.eslintignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/packages/app/script-utils/.eslintrc.json b/packages/app/script-utils/.eslintrc.json new file mode 100644 index 00000000..b7179966 --- /dev/null +++ b/packages/app/script-utils/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "extends": ["@lokalise/eslint-config/shared-package"], + "overrides": [ + { + "files": ["*.ts", "*.tsx"], + "parserOptions": { + "project": "./tsconfig.lint.json" + } + } + ] +} diff --git a/packages/app/script-utils/.gitignore b/packages/app/script-utils/.gitignore new file mode 100644 index 00000000..03f97ad9 --- /dev/null +++ b/packages/app/script-utils/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +coverage +.eslintcache diff --git a/packages/app/script-utils/.prettierignore b/packages/app/script-utils/.prettierignore new file mode 100644 index 00000000..ba2a97b5 --- /dev/null +++ b/packages/app/script-utils/.prettierignore @@ -0,0 +1,2 @@ +node_modules +coverage diff --git a/packages/app/script-utils/LICENSE.md b/packages/app/script-utils/LICENSE.md new file mode 100644 index 00000000..463dc9dc --- /dev/null +++ b/packages/app/script-utils/LICENSE.md @@ -0,0 +1,13 @@ +Copyright 2024 Lokalise, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/app/script-utils/README.md b/packages/app/script-utils/README.md new file mode 100644 index 00000000..d183acbe --- /dev/null +++ b/packages/app/script-utils/README.md @@ -0,0 +1,28 @@ +# Script utils + +# Usage + +```typescript +import { generateMonotonicUuid } from '@lokalise/id-utils' + +const newId = generateMonotonicUuid() +``` + +Provided ID conforms with an abstract UUID representation +(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) and is generated by ULID +algorithm, which makes it lexicographically sortable. You can use +basic language functions to sort by these ids: + +```typescript +import { generateMonotonicUuid } from '@lokalise/id-utils' + +const ids = [] + +for (const i of Array(1000)) { + ids.push(generateMonotonicUuid()) +} + +const sorted = ids.sort() + +// sorted == ids +``` diff --git a/packages/app/script-utils/package.json b/packages/app/script-utils/package.json new file mode 100644 index 00000000..30b82bbb --- /dev/null +++ b/packages/app/script-utils/package.json @@ -0,0 +1,51 @@ +{ + "name": "@lokalise/script-utils", + "version": "1.0.0", + "type": "module", + "files": [ + "dist", + "README.md", + "LICENSE.md" + ], + "license": "Apache-2.0", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "homepage": "https://github.com/lokalise/shared-ts-libs", + "repository": { + "type": "git", + "url": "git://github.com/lokalise/shared-ts-libs.git" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.cjs", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "rimraf dist && vite build", + "dev": "vite watch", + "clean": "rimraf dist .eslintcache", + "lint": "eslint --cache --max-warnings=0 && prettier --check --log-level warn src \"**/*.{json,md}\" && tsc --noEmit", + "lint:fix": "eslint --fix && prettier --write src \"**/*.{json,md}\"", + "test:ci": "vitest run --coverage", + "prepublishOnly": "npm run build", + "package-version": "echo $npm_package_version" + }, + "dependencies": { + "@lokalise/node-core": "^10.0.0" + }, + "devDependencies": { + "@lokalise/eslint-config": "latest", + "@lokalise/package-vite-config": "latest", + "@lokalise/prettier-config": "latest", + "@vitest/coverage-v8": "^1.6.0", + "prettier": "^3.3.1", + "rimraf": "^5.0.7", + "typescript": "5.4.5", + "vite": "^5.2.13", + "vitest": "^1.6.0" + }, + "prettier": "@lokalise/prettier-config" +} diff --git a/packages/app/script-utils/src/index.ts b/packages/app/script-utils/src/index.ts new file mode 100644 index 00000000..73416fd6 --- /dev/null +++ b/packages/app/script-utils/src/index.ts @@ -0,0 +1 @@ +export { synchronizeEnvFileWithVault } from './vault/syncEnvWithVault' diff --git a/packages/app/script-utils/src/vault/dir.ts b/packages/app/script-utils/src/vault/dir.ts new file mode 100644 index 00000000..835746c2 --- /dev/null +++ b/packages/app/script-utils/src/vault/dir.ts @@ -0,0 +1,7 @@ +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +export const rootDir = process.env.npm_config_local_prefix ?? resolve(__dirname, '..', '..') diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts new file mode 100644 index 00000000..115db53c --- /dev/null +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -0,0 +1,64 @@ +import { afterEach, describe, expect, it } from 'vitest' +import { updateEnvFile } from './syncEnvWithVault' +import { unlinkSync, readFileSync, writeFileSync } from 'node:fs' + +const DOT_ENV_PATH = __dirname + '/test_env' + +function readDotEnvFile() { + return readFileSync(DOT_ENV_PATH, { encoding: 'utf8' }).trim().split('\n') +} + +function putToDotEnvFile(lines: string[]) { + writeFileSync(DOT_ENV_PATH, lines.join('\n')) +} + +describe('sync env with vault', () => { + afterEach(() => { + unlinkSync(DOT_ENV_PATH) + }) + + it('should add env vars to file', () => { + updateEnvFile( + { + var1: 'value1', + var2: 'value2', + }, + DOT_ENV_PATH, + ) + + const content = readDotEnvFile() + + expect(content).toEqual(['var1=value1', 'var2=value2']) + }) + + it('should merge existing data in file with the one from input', () => { + putToDotEnvFile(['var0=value0', 'var3=value3']) + + updateEnvFile( + { + var1: 'value1', + var2: 'value2', + }, + DOT_ENV_PATH, + ) + + const content = readDotEnvFile() + + expect(content).toEqual(['var0=value0', 'var3=value3', 'var1=value1', 'var2=value2']) + }) + + it("should update value if exists in file", () => { + putToDotEnvFile(['var0=value0', 'var3=value3']) + + updateEnvFile( + { + var0: 'value1', + }, + DOT_ENV_PATH, + ) + + const content = readDotEnvFile() + + expect(content).toEqual(['var0=value1', 'var3=value3']) + }) +}) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.ts new file mode 100644 index 00000000..5903c057 --- /dev/null +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.ts @@ -0,0 +1,92 @@ +import { existsSync, readFileSync, writeFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { parseEnv } from 'node:util' +import { globalLogger } from '@lokalise/node-core' +import { rootDir } from './dir' +import { vaultGetVars, vaultLogin } from './vault' + +/** + * This function updates the contents of an .env file so the passed `key` has + * a new `value` while preserving the rest of the contents. If the key is not + * present, it will be appended to the end of the .env text. + * + * @param key The env variable to be updated + * @param value The new value for the env variable + * @param envContents String contents of the .env file + * @param parsedEnv Record of .env file key-values (parsed using `dotenv.parse`) + */ +const upsertEnvValue = ( + key: string, + value: string, + envContents: string, + parsedEnv: Record, +) => { + const formattedValue = value.includes('\n') ? `"${value.trim()}"` : value + + // Append to the end + if (!Object.keys(parsedEnv).includes(key)) { + const existingContent = envContents.trim().length > 0 ? `${envContents.trimEnd()}\n` : '' + return `${existingContent}${key}=${formattedValue}\n` + } + + // If variable is empty - set it + if (parsedEnv[key] === '') { + return envContents.replace(`${key}=`, `${key}=${formattedValue}`) + } + + // Else replace the value itself with adding quotes + return envContents.replace(parsedEnv[key], value.trim()) +} + +/** + * Updates the .env file with new variables. + * + * @param envVars Record of variable key-values + * @param file Path to the .env file + * @param createEmpty Create the file even if there are no variables to update + */ +export const updateEnvFile = ( + envVars: Record, + file: string, + createEmpty = false, +) => { + if (Object.entries(envVars).length === 0 && !createEmpty) { + globalLogger.info(`Skipping env file ${file}`) + return + } + let env = existsSync(file) ? readFileSync(file, { encoding: 'utf-8' }) : '' + const parsedEnv = parseEnv(env) as Record + + for (const [key, value] of Object.entries(envVars)) { + env = upsertEnvValue(key, value, env, parsedEnv) + } + + globalLogger.info(`Writing ${file}`) + writeFileSync(file, env, { encoding: 'utf-8' }) +} + +export const synchronizeEnvFileWithVault = ({ + dryRun, + vault, + vaultNamespace, + vaultUrl, + dotenvFilePath, +}: { + dryRun: boolean + vault: boolean + vaultNamespace: string + vaultUrl: string + dotenvFilePath: string +}) => { + if (vault) { + vaultLogin(vaultUrl) + } + const envVarsLocal = vaultGetVars(vaultNamespace) + + if (dryRun) { + globalLogger.info(envVarsLocal) + globalLogger.info(`// ${dotenvFilePath}`) + } else { + updateEnvFile(envVarsLocal, dotenvFilePath) + } +} diff --git a/packages/app/script-utils/src/vault/vault.ts b/packages/app/script-utils/src/vault/vault.ts new file mode 100644 index 00000000..2b9e690c --- /dev/null +++ b/packages/app/script-utils/src/vault/vault.ts @@ -0,0 +1,106 @@ +import type { SpawnSyncReturns } from 'node:child_process' +import { execSync } from 'node:child_process' + +import { globalLogger } from '@lokalise/node-core' + +type VaultGetTokenResponse = { + request_id: string + data: { + id: string + expire_time: string + } +} + +type VaultGetVarsResponse = { + request_id: string + data: { + data: Record + metadata: { + version: number + } + } +} + +const isExecError = (e: unknown): e is Error & SpawnSyncReturns => + typeof e === 'object' && e !== null && 'stderr' in e + +const installVaultCli = () => { + const osType = process.platform + if (osType === 'linux') { + try { + execSync('which vault') + } catch (_e) { + // if it fails means vault is not found -> install + execSync('sudo apt update && sudo apt install gpg') + execSync( + 'wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg', + ) + execSync( + 'echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list', + ) + execSync('sudo apt update && sudo apt install vault') + } + } else if (osType === 'darwin') { + // macOS + execSync('brew list vault || brew install vault') // installing only if not installed + } else { + globalLogger.warn( + `You are running ${osType}, which doesn't support autoinstall, please ensure that Vault is installed and is accessible globally via "vault" command.`, + ) + } +} + +// eslint-disable-next-line max-statements +export const vaultLogin = (vaultUrl: string) => { + process.env.VAULT_ADDR = vaultUrl + + let tokenResponse: VaultGetTokenResponse | null = null + + const loginCommand = 'vault login -method=oidc -path=gsuite -no-print' + const tokenCommand = 'vault token lookup -format=json' + try { + installVaultCli() + try { + // If the token doesn't exist or is expired --> login + tokenResponse = JSON.parse(execSync(tokenCommand).toString()) as VaultGetTokenResponse + + if (tokenResponse && new Date(tokenResponse.data.expire_time) <= new Date()) { + execSync(loginCommand) + tokenResponse = null + } + } catch (_e) { + execSync(loginCommand) + } + + if (!tokenResponse) { + tokenResponse = JSON.parse(execSync(tokenCommand).toString()) as VaultGetTokenResponse + } + } catch (e: unknown) { + if (e instanceof Error) { + globalLogger.info(`Vault login error -> ${e.message}`) + } + } + + process.env.VAULT_TOKEN = tokenResponse?.data.id ?? '' +} + +export const vaultGetVars = (service: string): Record => { + if (!process.env.VAULT_TOKEN) { + return {} + } + + try { + const responseJson = JSON.parse( + execSync(`vault kv get -format=json "kv/${service}/dev"`).toString(), + ) as VaultGetVarsResponse + + globalLogger.info(`Vault ${service} version: ${responseJson.data.metadata.version}`) + + return responseJson.data.data + } catch (e: unknown) { + if (isExecError(e)) { + globalLogger.error(`Vault ${service} error downloading env vars -> ${e.stderr.toString()}`) + } + return {} + } +} diff --git a/packages/app/script-utils/tsconfig.json b/packages/app/script-utils/tsconfig.json new file mode 100644 index 00000000..b0199dfa --- /dev/null +++ b/packages/app/script-utils/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "outDir": "dist", + "module": "ESNext", + "target": "ES2022", + "lib": ["ES2022", "dom"], + "sourceMap": false, + "declaration": true, + "declarationMap": false, + "types": ["vitest/globals"], + "skipLibCheck": true, + "strict": true, + "moduleResolution": "node", + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "strictNullChecks": true, + "importHelpers": true, + "baseUrl": "./", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/packages/app/script-utils/tsconfig.lint.json b/packages/app/script-utils/tsconfig.lint.json new file mode 100644 index 00000000..a88cd87f --- /dev/null +++ b/packages/app/script-utils/tsconfig.lint.json @@ -0,0 +1,5 @@ +{ + "extends": ["./tsconfig.json"], + "include": ["src/**/*", "vite.config.ts"], + "exclude": [] +} diff --git a/packages/app/script-utils/vite.config.ts b/packages/app/script-utils/vite.config.ts new file mode 100644 index 00000000..70b3e84f --- /dev/null +++ b/packages/app/script-utils/vite.config.ts @@ -0,0 +1,11 @@ +import { resolve } from 'path' + +import defineConfig from '@lokalise/package-vite-config/package' + +import packageJson from './package.json' + +/* eslint-disable import/no-default-export */ +export default defineConfig({ + entry: resolve(__dirname, 'src/index.ts'), + dependencies: Object.keys(packageJson.dependencies), +}) From a439d978dd1be0ce1a28e58583181a6d39ec9219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 12:11:58 +0200 Subject: [PATCH 02/20] AP-3965 Dir not needed. --- packages/app/script-utils/src/vault/dir.ts | 7 ------- packages/app/script-utils/src/vault/syncEnvWithVault.ts | 2 -- 2 files changed, 9 deletions(-) delete mode 100644 packages/app/script-utils/src/vault/dir.ts diff --git a/packages/app/script-utils/src/vault/dir.ts b/packages/app/script-utils/src/vault/dir.ts deleted file mode 100644 index 835746c2..00000000 --- a/packages/app/script-utils/src/vault/dir.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { dirname, resolve } from 'node:path' -import { fileURLToPath } from 'node:url' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = dirname(__filename) - -export const rootDir = process.env.npm_config_local_prefix ?? resolve(__dirname, '..', '..') diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.ts index 5903c057..ed9e77aa 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.ts @@ -1,8 +1,6 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs' -import { resolve } from 'node:path' import { parseEnv } from 'node:util' import { globalLogger } from '@lokalise/node-core' -import { rootDir } from './dir' import { vaultGetVars, vaultLogin } from './vault' /** From c95c5e702481b37b41563abf8a4eea6bcba8fa31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 12:29:05 +0200 Subject: [PATCH 03/20] AP-3965 Updated script utils readme. --- packages/app/script-utils/README.md | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/app/script-utils/README.md b/packages/app/script-utils/README.md index d183acbe..40e8a9d6 100644 --- a/packages/app/script-utils/README.md +++ b/packages/app/script-utils/README.md @@ -2,27 +2,18 @@ # Usage -```typescript -import { generateMonotonicUuid } from '@lokalise/id-utils' - -const newId = generateMonotonicUuid() -``` - -Provided ID conforms with an abstract UUID representation -(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) and is generated by ULID -algorithm, which makes it lexicographically sortable. You can use -basic language functions to sort by these ids: +## Syncing vault secrets with .env file ```typescript -import { generateMonotonicUuid } from '@lokalise/id-utils' +//sync-with-vault.ts +import { synchronizeEnvFileWithVault } from '@lokalise/script-utils' -const ids = [] +//Use this function to sync .env file with vault secrets, provide all params to the function. +``` -for (const i of Array(1000)) { - ids.push(generateMonotonicUuid()) +Later in `package.json`, `tsx` is recommended to be used as a script runner, so you can add the following script to your `package.json` file: +``` +"scripts": { + "sync-with-vault": "tsx sync-with-vault.ts" } - -const sorted = ids.sort() - -// sorted == ids ``` From 38632fe282bcf06f8797c2a14724cc3cf64ea201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 12:31:36 +0200 Subject: [PATCH 04/20] AP-3965 Updated script utils readme. --- packages/app/script-utils/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/script-utils/README.md b/packages/app/script-utils/README.md index 40e8a9d6..6df78fd7 100644 --- a/packages/app/script-utils/README.md +++ b/packages/app/script-utils/README.md @@ -11,7 +11,7 @@ import { synchronizeEnvFileWithVault } from '@lokalise/script-utils' //Use this function to sync .env file with vault secrets, provide all params to the function. ``` -Later in `package.json`, `tsx` is recommended to be used as a script runner, so you can add the following script to your `package.json` file: +`tsx` is recommended to be used as a script runner, so you can add the following script to your `package.json` file: ``` "scripts": { "sync-with-vault": "tsx sync-with-vault.ts" From db4f452e491978368e2023fa91df8aacd9c765b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 12:31:59 +0200 Subject: [PATCH 05/20] AP-3965 Vite node:path --- packages/app/script-utils/vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/script-utils/vite.config.ts b/packages/app/script-utils/vite.config.ts index 70b3e84f..e55d8b19 100644 --- a/packages/app/script-utils/vite.config.ts +++ b/packages/app/script-utils/vite.config.ts @@ -1,4 +1,4 @@ -import { resolve } from 'path' +import { resolve } from 'node:path' import defineConfig from '@lokalise/package-vite-config/package' From 4a4024e74eefe6977344d13da02099da32b2589c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 12:33:20 +0200 Subject: [PATCH 06/20] AP-3965 Changed to node:path everywhere. --- packages/app/api-common/vite.config.ts | 2 +- packages/app/events-common/vite.config.ts | 2 +- packages/app/id-utils/vite.config.ts | 2 +- packages/app/non-translatable-markup/vite.config.ts | 2 +- packages/app/prisma-utils/vite.config.ts | 2 +- packages/app/websockets-common/vite.config.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/app/api-common/vite.config.ts b/packages/app/api-common/vite.config.ts index e220dfa8..7eebe9e5 100644 --- a/packages/app/api-common/vite.config.ts +++ b/packages/app/api-common/vite.config.ts @@ -1,4 +1,4 @@ -import { resolve } from 'path' +import { resolve } from 'node:path' import defineConfig from '@lokalise/package-vite-config/package' diff --git a/packages/app/events-common/vite.config.ts b/packages/app/events-common/vite.config.ts index 757f21b3..490254dc 100644 --- a/packages/app/events-common/vite.config.ts +++ b/packages/app/events-common/vite.config.ts @@ -1,4 +1,4 @@ -import { resolve } from 'path' +import { resolve } from 'node:path' import defineConfig from '@lokalise/package-vite-config/package' diff --git a/packages/app/id-utils/vite.config.ts b/packages/app/id-utils/vite.config.ts index 70b3e84f..e55d8b19 100644 --- a/packages/app/id-utils/vite.config.ts +++ b/packages/app/id-utils/vite.config.ts @@ -1,4 +1,4 @@ -import { resolve } from 'path' +import { resolve } from 'node:path' import defineConfig from '@lokalise/package-vite-config/package' diff --git a/packages/app/non-translatable-markup/vite.config.ts b/packages/app/non-translatable-markup/vite.config.ts index e220dfa8..7eebe9e5 100644 --- a/packages/app/non-translatable-markup/vite.config.ts +++ b/packages/app/non-translatable-markup/vite.config.ts @@ -1,4 +1,4 @@ -import { resolve } from 'path' +import { resolve } from 'node:path' import defineConfig from '@lokalise/package-vite-config/package' diff --git a/packages/app/prisma-utils/vite.config.ts b/packages/app/prisma-utils/vite.config.ts index 70b3e84f..e55d8b19 100644 --- a/packages/app/prisma-utils/vite.config.ts +++ b/packages/app/prisma-utils/vite.config.ts @@ -1,4 +1,4 @@ -import { resolve } from 'path' +import { resolve } from 'node:path' import defineConfig from '@lokalise/package-vite-config/package' diff --git a/packages/app/websockets-common/vite.config.ts b/packages/app/websockets-common/vite.config.ts index cdf0cb21..4fd145b2 100644 --- a/packages/app/websockets-common/vite.config.ts +++ b/packages/app/websockets-common/vite.config.ts @@ -1,4 +1,4 @@ -import { resolve } from 'path' +import { resolve } from 'node:path' import defineConfig from '@lokalise/package-vite-config/package' From 62a4baf48bea11250768bda42fe05505b5bd85ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 12:36:05 +0200 Subject: [PATCH 07/20] AP-3965 lint --- packages/app/script-utils/README.md | 1 + .../src/vault/syncEnvWithVault.spec.ts | 22 +++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/app/script-utils/README.md b/packages/app/script-utils/README.md index 6df78fd7..8ab93fcf 100644 --- a/packages/app/script-utils/README.md +++ b/packages/app/script-utils/README.md @@ -12,6 +12,7 @@ import { synchronizeEnvFileWithVault } from '@lokalise/script-utils' ``` `tsx` is recommended to be used as a script runner, so you can add the following script to your `package.json` file: + ``` "scripts": { "sync-with-vault": "tsx sync-with-vault.ts" diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index 115db53c..4f96c710 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -47,18 +47,18 @@ describe('sync env with vault', () => { expect(content).toEqual(['var0=value0', 'var3=value3', 'var1=value1', 'var2=value2']) }) - it("should update value if exists in file", () => { - putToDotEnvFile(['var0=value0', 'var3=value3']) + it('should update value if exists in file', () => { + putToDotEnvFile(['var0=value0', 'var3=value3']) - updateEnvFile( - { - var0: 'value1', - }, - DOT_ENV_PATH, - ) + updateEnvFile( + { + var0: 'value1', + }, + DOT_ENV_PATH, + ) - const content = readDotEnvFile() + const content = readDotEnvFile() - expect(content).toEqual(['var0=value1', 'var3=value3']) - }) + expect(content).toEqual(['var0=value1', 'var3=value3']) + }) }) From 8a8dc87bd0905f27f3495dbe58f62d852b09983c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 12:57:35 +0200 Subject: [PATCH 08/20] AP-3965 Ignore vault file. --- packages/app/script-utils/src/vault/vault.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/app/script-utils/src/vault/vault.ts b/packages/app/script-utils/src/vault/vault.ts index 2b9e690c..e13ecc3a 100644 --- a/packages/app/script-utils/src/vault/vault.ts +++ b/packages/app/script-utils/src/vault/vault.ts @@ -1,3 +1,4 @@ +/* c8 ignore start */ import type { SpawnSyncReturns } from 'node:child_process' import { execSync } from 'node:child_process' @@ -104,3 +105,4 @@ export const vaultGetVars = (service: string): Record => { return {} } } +/* c8 ignore end */ From 91c98609252e787eb9b533ef67ae7df82f498dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 13:05:29 +0200 Subject: [PATCH 09/20] AP-3965 Improving code coverage. --- .../src/vault/syncEnvWithVault.spec.ts | 17 +++++++++++++++-- .../script-utils/src/vault/syncEnvWithVault.ts | 4 +--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index 4f96c710..6e1ae4c1 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest' import { updateEnvFile } from './syncEnvWithVault' -import { unlinkSync, readFileSync, writeFileSync } from 'node:fs' +import { unlinkSync, readFileSync, writeFileSync, existsSync } from 'node:fs' const DOT_ENV_PATH = __dirname + '/test_env' @@ -14,7 +14,9 @@ function putToDotEnvFile(lines: string[]) { describe('sync env with vault', () => { afterEach(() => { - unlinkSync(DOT_ENV_PATH) + if (existsSync(DOT_ENV_PATH)) { + unlinkSync(DOT_ENV_PATH) + } }) it('should add env vars to file', () => { @@ -61,4 +63,15 @@ describe('sync env with vault', () => { expect(content).toEqual(['var0=value1', 'var3=value3']) }) + + it("should do nothing if provided env vars are empty", ()=> { + updateEnvFile( + { + }, + DOT_ENV_PATH, + ) + + expect(existsSync(DOT_ENV_PATH)).toEqual(false) + }) + }) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.ts index ed9e77aa..b4bd8d0f 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.ts @@ -41,14 +41,12 @@ const upsertEnvValue = ( * * @param envVars Record of variable key-values * @param file Path to the .env file - * @param createEmpty Create the file even if there are no variables to update */ export const updateEnvFile = ( envVars: Record, file: string, - createEmpty = false, ) => { - if (Object.entries(envVars).length === 0 && !createEmpty) { + if (Object.entries(envVars).length === 0) { globalLogger.info(`Skipping env file ${file}`) return } From 4b50a879f372a798ad216bac3d7dd80cc7fc2d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 13:09:33 +0200 Subject: [PATCH 10/20] AP-3965 Improving code coverage. --- .../app/script-utils/src/vault/syncEnvWithVault.spec.ts | 9 ++------- packages/app/script-utils/src/vault/syncEnvWithVault.ts | 5 +---- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index 6e1ae4c1..d9468da4 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -64,14 +64,9 @@ describe('sync env with vault', () => { expect(content).toEqual(['var0=value1', 'var3=value3']) }) - it("should do nothing if provided env vars are empty", ()=> { - updateEnvFile( - { - }, - DOT_ENV_PATH, - ) + it('should do nothing if provided env vars are empty', () => { + updateEnvFile({}, DOT_ENV_PATH) expect(existsSync(DOT_ENV_PATH)).toEqual(false) }) - }) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.ts index b4bd8d0f..971d9a9b 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.ts @@ -42,10 +42,7 @@ const upsertEnvValue = ( * @param envVars Record of variable key-values * @param file Path to the .env file */ -export const updateEnvFile = ( - envVars: Record, - file: string, -) => { +export const updateEnvFile = (envVars: Record, file: string) => { if (Object.entries(envVars).length === 0) { globalLogger.info(`Skipping env file ${file}`) return From 5e058649a61a9457e54e6a630919414a99d5a698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 13:15:16 +0200 Subject: [PATCH 11/20] AP-3965 Improving code coverage. --- packages/app/script-utils/src/vault/syncEnvWithVault.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.ts index 971d9a9b..33cea31d 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.ts @@ -58,6 +58,7 @@ export const updateEnvFile = (envVars: Record, file: string) => writeFileSync(file, env, { encoding: 'utf-8' }) } +/* c8 ignore start */ export const synchronizeEnvFileWithVault = ({ dryRun, vault, @@ -83,3 +84,4 @@ export const synchronizeEnvFileWithVault = ({ updateEnvFile(envVarsLocal, dotenvFilePath) } } +/* c8 ignore end */ From 45da15b7ac1f12035513fc2323227971d855119e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 13:26:14 +0200 Subject: [PATCH 12/20] AP-3965 Improving code coverage. --- .../src/vault/syncEnvWithVault.spec.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index d9468da4..0edcf269 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -69,4 +69,19 @@ describe('sync env with vault', () => { expect(existsSync(DOT_ENV_PATH)).toEqual(false) }) + + it("should replace value of key if in file it is empty", () => { + putToDotEnvFile(['var0=', 'var3=value3']) + + updateEnvFile( + { + var0: 'value0', + }, + DOT_ENV_PATH, + ) + + const content = readDotEnvFile() + + expect(content).toEqual(['var0=value0', 'var3=value3']) + }) }) From 62eb3c1e93f05a1addfb41c05644c7fc0d8ce3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 13:29:00 +0200 Subject: [PATCH 13/20] AP-3965 Improving code coverage. --- packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index 0edcf269..e8a28cd1 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -70,7 +70,7 @@ describe('sync env with vault', () => { expect(existsSync(DOT_ENV_PATH)).toEqual(false) }) - it("should replace value of key if in file it is empty", () => { + it('should replace value of key if in file it is empty', () => { putToDotEnvFile(['var0=', 'var3=value3']) updateEnvFile( From 0620f6bc8b336493e453b2369ff1599fdcf4d841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 14:27:43 +0200 Subject: [PATCH 14/20] AP-3965 Improving code coverage. --- .../src/vault/syncEnvWithVault.spec.ts | 21 ++++++++++++++++++- .../src/vault/syncEnvWithVault.ts | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index e8a28cd1..bb1bc1ac 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -5,7 +5,12 @@ import { unlinkSync, readFileSync, writeFileSync, existsSync } from 'node:fs' const DOT_ENV_PATH = __dirname + '/test_env' function readDotEnvFile() { - return readFileSync(DOT_ENV_PATH, { encoding: 'utf8' }).trim().split('\n') + let rawcontent = readFileSync(DOT_ENV_PATH, { encoding: 'utf8' }); + + console.log(rawcontent) + return rawcontent + .trim() + .split('\n') } function putToDotEnvFile(lines: string[]) { @@ -84,4 +89,18 @@ describe('sync env with vault', () => { expect(content).toEqual(['var0=value0', 'var3=value3']) }) + + it("should wrap value in quotes if vault value has new line", () => { + updateEnvFile( + { + var0: 'value\nwith new line', + }, + DOT_ENV_PATH, + ) + + const content = readDotEnvFile() + console.log(content) + expect(content).toEqual(['var0=value0', 'var3=value3']) + + }) }) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.ts index 33cea31d..eb73acb9 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.ts @@ -19,6 +19,7 @@ const upsertEnvValue = ( envContents: string, parsedEnv: Record, ) => { + /* c8 ignore next */ const formattedValue = value.includes('\n') ? `"${value.trim()}"` : value // Append to the end From bac9255ff4284add89640467bebe17035634d8a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 14:29:46 +0200 Subject: [PATCH 15/20] AP-3965 lint --- .../app/script-utils/src/vault/syncEnvWithVault.spec.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index bb1bc1ac..3a00315b 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -5,12 +5,10 @@ import { unlinkSync, readFileSync, writeFileSync, existsSync } from 'node:fs' const DOT_ENV_PATH = __dirname + '/test_env' function readDotEnvFile() { - let rawcontent = readFileSync(DOT_ENV_PATH, { encoding: 'utf8' }); + let rawcontent = readFileSync(DOT_ENV_PATH, { encoding: 'utf8' }) console.log(rawcontent) - return rawcontent - .trim() - .split('\n') + return rawcontent.trim().split('\n') } function putToDotEnvFile(lines: string[]) { @@ -90,7 +88,7 @@ describe('sync env with vault', () => { expect(content).toEqual(['var0=value0', 'var3=value3']) }) - it("should wrap value in quotes if vault value has new line", () => { + it('should wrap value in quotes if vault value has new line', () => { updateEnvFile( { var0: 'value\nwith new line', @@ -101,6 +99,5 @@ describe('sync env with vault', () => { const content = readDotEnvFile() console.log(content) expect(content).toEqual(['var0=value0', 'var3=value3']) - }) }) From 3e70ce55934745884cab6163ef108070caf2f4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 14:34:50 +0200 Subject: [PATCH 16/20] AP-3965 reverted test --- .../script-utils/src/vault/syncEnvWithVault.spec.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index 3a00315b..47d2457b 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -88,16 +88,4 @@ describe('sync env with vault', () => { expect(content).toEqual(['var0=value0', 'var3=value3']) }) - it('should wrap value in quotes if vault value has new line', () => { - updateEnvFile( - { - var0: 'value\nwith new line', - }, - DOT_ENV_PATH, - ) - - const content = readDotEnvFile() - console.log(content) - expect(content).toEqual(['var0=value0', 'var3=value3']) - }) }) From cf369425b4028630da09d754f2289952bada39d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 14:36:52 +0200 Subject: [PATCH 17/20] AP-3965 lint --- packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index 47d2457b..e2610306 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -87,5 +87,4 @@ describe('sync env with vault', () => { expect(content).toEqual(['var0=value0', 'var3=value3']) }) - }) From ebc6d0bd515e892683022c75f0d92c477c59fd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 14:39:40 +0200 Subject: [PATCH 18/20] AP-3965 Improving code coverage. --- packages/app/script-utils/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/app/script-utils/src/index.ts b/packages/app/script-utils/src/index.ts index 73416fd6..e4eb9826 100644 --- a/packages/app/script-utils/src/index.ts +++ b/packages/app/script-utils/src/index.ts @@ -1 +1,2 @@ +/* c8 ignore next */ export { synchronizeEnvFileWithVault } from './vault/syncEnvWithVault' From 7803544dfcb74c282e83c3add5b41e3dfa79b804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 15:59:28 +0200 Subject: [PATCH 19/20] AP-3965 Release workflow for script-utils. --- .github/workflows/release.script-utils.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/release.script-utils.yml diff --git a/.github/workflows/release.script-utils.yml b/.github/workflows/release.script-utils.yml new file mode 100644 index 00000000..1688bf64 --- /dev/null +++ b/.github/workflows/release.script-utils.yml @@ -0,0 +1,18 @@ +name: Package release to NPM -> script-utils +on: + pull_request: + types: + - closed + branches: + - main + paths: + - 'packages/app/script-utils/**' + +jobs: + call-build-flow: + uses: lokalise/shared-ts-libs/.github/workflows/release.package.yml@main + with: + working_directory: 'packages/app/script-utils' + package_name: 'script-utils' + secrets: + npm_token: ${{ secrets.NPM_TOKEN }} From a95e354cc63c4a4e8f673ec916202ca319d1c6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wylega=C5=82a?= Date: Mon, 17 Jun 2024 16:07:39 +0200 Subject: [PATCH 20/20] AP-3965 Removed console.log --- .../app/script-utils/src/vault/syncEnvWithVault.spec.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts index e2610306..8d4936dd 100644 --- a/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts +++ b/packages/app/script-utils/src/vault/syncEnvWithVault.spec.ts @@ -1,14 +1,11 @@ import { afterEach, describe, expect, it } from 'vitest' import { updateEnvFile } from './syncEnvWithVault' -import { unlinkSync, readFileSync, writeFileSync, existsSync } from 'node:fs' +import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs' const DOT_ENV_PATH = __dirname + '/test_env' function readDotEnvFile() { - let rawcontent = readFileSync(DOT_ENV_PATH, { encoding: 'utf8' }) - - console.log(rawcontent) - return rawcontent.trim().split('\n') + return readFileSync(DOT_ENV_PATH, { encoding: 'utf8' }).trim().split('\n') } function putToDotEnvFile(lines: string[]) {