@tmlmobilidade/env-sync 20260304.1625.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # Environment Sync CLI
2
+
3
+ CLI tool to sync production and staging environments for MongoDB and Storage (using RClone).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install
9
+ npm run build
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ### Basic Usage
15
+
16
+ ```bash
17
+ # Sync both MongoDB and Storage (interactive mode)
18
+ npm run dev
19
+
20
+ # Run with arguments (use -- to pass arguments to the script)
21
+ npm run dev -- --help
22
+ npm run dev -- --db-only
23
+ npm run dev -- --storage-only
24
+
25
+ # Or after building:
26
+ ./dist/index.js
27
+ ./dist/index.js --help
28
+ ```
29
+
30
+ ### Command Line Options
31
+
32
+ ```bash
33
+ # Sync only MongoDB database
34
+ env-sync --db-only
35
+
36
+ # Sync only storage
37
+ env-sync --storage-only
38
+
39
+ # Use replica set mode
40
+ env-sync --replica-set
41
+
42
+ # Skip cleanup of old backups
43
+ env-sync --no-cleanup
44
+
45
+ # Upload backup artifacts to OCI bucket (for CI/CD, replaces GitHub artifacts)
46
+ env-sync --db-only --upload-artifacts
47
+
48
+ # Show help
49
+ env-sync --help
50
+ ```
51
+
52
+ ## Configuration
53
+
54
+ Create a `.env` file in the `cli/env-sync-ts/` directory with the following variables:
55
+
56
+ ### MongoDB Configuration
57
+
58
+ ```env
59
+ # Production MongoDB
60
+ PROD_HOST=production-mongo-host:27017
61
+ PROD_USERNAME=admin
62
+ PROD_PASSWORD=password
63
+ PROD_AUTH_DATABASE=admin
64
+ PROD_DB=production_database
65
+
66
+ # Staging MongoDB
67
+ STAGING_HOST=staging-mongo-host:27017
68
+ STAGING_USERNAME=admin
69
+ STAGING_PASSWORD=password
70
+ STAGING_AUTH_DATABASE=admin
71
+ STAGING_DB=staging_database
72
+
73
+ # Optional: Collections to exclude from sync (space-separated)
74
+ EXCLUDE_COLLECTIONS=logs sessions temp_data
75
+
76
+ # Optional: Backup retention days (default: 7)
77
+ BACKUP_RETENTION_DAYS=7
78
+ ```
79
+
80
+ ### Storage Configuration (OCI/RClone)
81
+
82
+ ```env
83
+ # RClone Configuration
84
+ STORAGE_REMOTE_NAME=oci_storage
85
+ STORAGE_TYPE=oracleobjectstorage
86
+ STORAGE_SOURCE=production-bucket/path/to/source
87
+ STORAGE_DEST=staging-bucket/path/to/dest
88
+
89
+ # OCI Authentication
90
+ OCI_USER=ocid1.user.oc1..
91
+ OCI_FINGERPRINT=aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99
92
+ OCI_KEY_FILE=/path/to/private_key.pem
93
+ OCI_TENANCY=ocid1.tenancy.oc1..
94
+ OCI_REGION=us-ashburn-1
95
+ OCI_COMPARTMENT=ocid1.compartment.oc1..
96
+ OCI_NAMESPACE=your_namespace
97
+ ```
98
+
99
+ ### Artifacts Configuration (for CI/CD)
100
+
101
+ ```env
102
+ # OCI bucket for storing backup artifacts (required for --upload-artifacts)
103
+ ARTIFACTS_BUCKET=your-artifacts-bucket
104
+
105
+ # Optional: prefix/folder within the bucket (default: "env-sync")
106
+ ARTIFACTS_PREFIX=env-sync
107
+ ```
108
+
109
+ ## MongoDB Backup Strategy
110
+
111
+ - Full database dump with all collections (excluding configured collections)
112
+ - Dumps all collections from production database
113
+ - Restores to staging database with `--drop` flag
114
+
115
+ ### Backup Metadata
116
+ - Backup metadata is stored in `backups/.backup_metadata`
117
+ - Tracks last backup timestamp
118
+
119
+ ## Storage Sync
120
+
121
+ - Uses RClone to sync files from production OCI storage to staging
122
+ - Configured via environment variables (no rclone config file needed)
123
+ - Progress reporting during sync
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ # Run in development mode
129
+ npm run dev
130
+
131
+ # Build
132
+ npm run build
133
+
134
+ # Lint
135
+ npm run lint
136
+ ```
137
+
138
+ ## Requirements
139
+
140
+ - Node.js (v18+)
141
+ - MongoDB tools (`mongodump`, `mongorestore`)
142
+ - RClone (`rclone`)
143
+
144
+ ## Notes
145
+
146
+ - The script maintains compatibility with the existing bash scripts' `.env` file format
147
+ - Backups are stored in `backups/` directory
148
+ - Old backups are automatically cleaned up (configurable retention period)
149
+ - Interactive prompts use Clack for a modern CLI experience
150
+
@@ -0,0 +1,17 @@
1
+ import type { StorageConfig } from '../config/config-loader.js';
2
+ export interface UploadArtifactsOptions {
3
+ /** OCI bucket name to upload to */
4
+ bucket: string;
5
+ /** Local directory containing artifacts to upload */
6
+ localPath: string;
7
+ /** Optional prefix/folder within the bucket */
8
+ prefix?: string;
9
+ /** Storage config with OCI credentials */
10
+ storageConfig: StorageConfig;
11
+ }
12
+ /**
13
+ * Upload artifacts (backups) to OCI Object Storage bucket.
14
+ * This replaces the GitHub artifacts upload for security in public repos.
15
+ * The backup folder is zipped before uploading to reduce size and upload time.
16
+ */
17
+ export declare function uploadArtifacts(options: UploadArtifactsOptions): Promise<void>;
@@ -0,0 +1,181 @@
1
+ import archiver from 'archiver';
2
+ import { createWriteStream, existsSync, readdirSync, rmSync, statSync, writeFileSync } from 'fs';
3
+ import os from 'os';
4
+ import path from 'path';
5
+ import { checkCommandAvailable, execCommandStream } from '../utils/exec.js';
6
+ import { logger } from '../utils/logger.js';
7
+ /**
8
+ * Build a temporary OCI CLI config file using the credentials from StorageConfig.
9
+ * This lets rclone use the "user_principal_auth" provider without needing ~/.oci/config.
10
+ */
11
+ function buildOciConfig(config, profileName) {
12
+ return [
13
+ `[${profileName}]`,
14
+ `user=${config.user}`,
15
+ `fingerprint=${config.fingerprint}`,
16
+ `key_file=${config.keyFile}`,
17
+ `tenancy=${config.tenancy}`,
18
+ `region=${config.region}`,
19
+ '',
20
+ ].join('\n');
21
+ }
22
+ /**
23
+ * Build the rclone backend config pointing at the temporary OCI config file.
24
+ */
25
+ function buildRcloneConfig(config, ociConfigPath, profileName) {
26
+ return [
27
+ `[${config.remoteName}]`,
28
+ `type = ${config.type}`,
29
+ `namespace = ${config.namespace}`,
30
+ `compartment = ${config.compartment}`,
31
+ `region = ${config.region}`,
32
+ 'provider = user_principal_auth',
33
+ `config_file = ${ociConfigPath}`,
34
+ `config_profile = ${profileName}`,
35
+ '',
36
+ ].join('\n');
37
+ }
38
+ /**
39
+ * Check if the directory is empty or has no files to upload
40
+ */
41
+ function isDirectoryEmpty(dirPath) {
42
+ if (!existsSync(dirPath)) {
43
+ return true;
44
+ }
45
+ const stats = statSync(dirPath);
46
+ if (!stats.isDirectory()) {
47
+ return false;
48
+ }
49
+ const entries = readdirSync(dirPath);
50
+ // Filter out hidden files like .backup_metadata
51
+ const files = entries.filter((entry) => {
52
+ const entryPath = path.join(dirPath, entry);
53
+ const entryStats = statSync(entryPath);
54
+ return entryStats.isFile();
55
+ });
56
+ return files.length === 0;
57
+ }
58
+ /**
59
+ * Create a zip archive of the backup directory using archiver
60
+ */
61
+ async function createZipArchive(sourceDir, outputPath) {
62
+ logger.info(`Creating zip archive: ${outputPath}`);
63
+ return new Promise((resolve, reject) => {
64
+ const output = createWriteStream(outputPath);
65
+ const archive = archiver('zip', {
66
+ zlib: { level: 9 }, // Maximum compression
67
+ });
68
+ // Listen for all archive data to be written
69
+ output.on('close', () => {
70
+ logger.verbose(`Zip archive created successfully: ${outputPath} (${archive.pointer()} bytes)`);
71
+ resolve();
72
+ });
73
+ // Catch warnings (e.g., stat failures and other non-blocking errors)
74
+ archive.on('warning', (err) => {
75
+ if (err.code === 'ENOENT') {
76
+ logger.verbose(`Archive warning: ${err.message}`);
77
+ }
78
+ else {
79
+ reject(err);
80
+ }
81
+ });
82
+ // Catch errors
83
+ archive.on('error', (err) => {
84
+ reject(new Error(`Failed to create zip archive: ${err.message}`));
85
+ });
86
+ // Pipe archive data to the file
87
+ archive.pipe(output);
88
+ // Append the entire directory to the archive
89
+ const folderName = path.basename(sourceDir);
90
+ archive.directory(sourceDir, folderName);
91
+ // Finalize the archive (i.e., we are done appending files)
92
+ archive.finalize();
93
+ });
94
+ }
95
+ /**
96
+ * Upload artifacts (backups) to OCI Object Storage bucket.
97
+ * This replaces the GitHub artifacts upload for security in public repos.
98
+ * The backup folder is zipped before uploading to reduce size and upload time.
99
+ */
100
+ export async function uploadArtifacts(options) {
101
+ const { bucket, localPath, prefix, storageConfig } = options;
102
+ logger.info('Starting artifact upload to OCI Object Storage...');
103
+ // Check if local path exists
104
+ if (!existsSync(localPath)) {
105
+ logger.warn(`Artifact path does not exist: ${localPath}`);
106
+ logger.info('No artifacts to upload.');
107
+ return;
108
+ }
109
+ // Check if directory is empty
110
+ if (isDirectoryEmpty(localPath)) {
111
+ logger.warn(`Artifact directory is empty: ${localPath}`);
112
+ logger.info('No artifacts to upload.');
113
+ return;
114
+ }
115
+ // Check if rclone is available
116
+ if (!(await checkCommandAvailable('rclone'))) {
117
+ throw new Error('rclone not found. Please install rclone.');
118
+ }
119
+ // Create zip archive
120
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
121
+ const zipFileName = `backups-${timestamp}.zip`;
122
+ const zipFilePath = path.join(os.tmpdir(), zipFileName);
123
+ let zipCreated = false;
124
+ try {
125
+ await createZipArchive(localPath, zipFilePath);
126
+ zipCreated = true;
127
+ if (existsSync(zipFilePath)) {
128
+ const zipSizeMB = Math.round(statSync(zipFilePath).size / 1024 / 1024 * 100) / 100;
129
+ logger.info(`Zip archive created: ${zipFileName} (${zipSizeMB} MB)`);
130
+ }
131
+ else {
132
+ logger.info(`Zip archive created: ${zipFileName}`);
133
+ }
134
+ }
135
+ catch (error) {
136
+ logger.error(`Failed to create zip archive: ${error instanceof Error ? error.message : String(error)}`);
137
+ throw error;
138
+ }
139
+ // Prepare temporary OCI config
140
+ logger.info('Building temporary OCI config...');
141
+ const profileName = 'Default';
142
+ const ociConfigFile = path.join(os.tmpdir(), `oci-artifacts-${Date.now()}.conf`);
143
+ const ociConfigContent = buildOciConfig(storageConfig, profileName);
144
+ writeFileSync(ociConfigFile, ociConfigContent);
145
+ // Build rclone config pointing at the temporary OCI config
146
+ logger.info('Building rclone config...');
147
+ const rcloneConfig = buildRcloneConfig(storageConfig, ociConfigFile, profileName);
148
+ const rcloneConfigFile = path.join(os.tmpdir(), `rclone-artifacts-${Date.now()}.conf`);
149
+ writeFileSync(rcloneConfigFile, rcloneConfig);
150
+ // Build the destination path
151
+ const destFolder = prefix ?? '';
152
+ const destPath = `${storageConfig.remoteName}:${bucket}/${destFolder}`;
153
+ logger.info(`Uploading zip archive to: ${destPath}`);
154
+ // Use rclone copy to upload the zip file
155
+ const baseCmd = `rclone copy "${zipFilePath}" "${destPath}/" --config ${rcloneConfigFile}`;
156
+ const verboseFlags = logger.isVerbose() ? ' --progress --verbose' : '';
157
+ const copyCmd = `${baseCmd}${verboseFlags}`;
158
+ try {
159
+ await execCommandStream(copyCmd);
160
+ logger.success(`Artifacts uploaded successfully to: ${destPath}/${zipFileName}`);
161
+ // Clean up temporary files
162
+ rmSync(ociConfigFile);
163
+ rmSync(rcloneConfigFile);
164
+ if (zipCreated && existsSync(zipFilePath)) {
165
+ rmSync(zipFilePath);
166
+ logger.verbose('Temporary zip file cleaned up');
167
+ }
168
+ }
169
+ catch (error) {
170
+ // Clean up on error too
171
+ if (existsSync(ociConfigFile))
172
+ rmSync(ociConfigFile);
173
+ if (existsSync(rcloneConfigFile))
174
+ rmSync(rcloneConfigFile);
175
+ if (zipCreated && existsSync(zipFilePath)) {
176
+ rmSync(zipFilePath);
177
+ }
178
+ logger.error(`Artifact upload failed: ${error instanceof Error ? error.message : String(error)}`);
179
+ throw error;
180
+ }
181
+ }
@@ -0,0 +1,12 @@
1
+ export interface CliOptions {
2
+ backupOnly?: boolean;
3
+ dbOnly?: boolean;
4
+ help?: boolean;
5
+ noCleanup?: boolean;
6
+ replicaSet?: boolean;
7
+ storageOnly?: boolean;
8
+ uploadArtifacts?: boolean;
9
+ verbose?: boolean;
10
+ }
11
+ export declare function parseArgs(args: string[]): CliOptions;
12
+ export declare function showHelp(): void;
@@ -0,0 +1,111 @@
1
+ export function parseArgs(args) {
2
+ const options = {};
3
+ for (const arg of args) {
4
+ switch (arg) {
5
+ case '--backup-only':
6
+ options.backupOnly = true;
7
+ break;
8
+ case '--db-only':
9
+ options.dbOnly = true;
10
+ break;
11
+ case '--help':
12
+ case '-h':
13
+ options.help = true;
14
+ break;
15
+ case '--no-cleanup':
16
+ options.noCleanup = true;
17
+ break;
18
+ case '--no-replica-set':
19
+ options.replicaSet = false;
20
+ break;
21
+ case '--replica-set':
22
+ options.replicaSet = true;
23
+ break;
24
+ case '--storage-only':
25
+ options.storageOnly = true;
26
+ break;
27
+ case '--upload-artifacts':
28
+ options.uploadArtifacts = true;
29
+ break;
30
+ case '--verbose':
31
+ case '-v':
32
+ options.verbose = true;
33
+ break;
34
+ default:
35
+ if (arg.startsWith('-')) {
36
+ throw new Error(`Unknown option: ${arg}\nRun 'env-sync --help' for more information.`);
37
+ }
38
+ break;
39
+ }
40
+ }
41
+ return options;
42
+ }
43
+ export function showHelp() {
44
+ console.log(`
45
+ Environment Sync CLI - Production to Staging
46
+
47
+ SYNOPSIS
48
+ env-sync [OPTIONS]
49
+
50
+ DESCRIPTION
51
+ Syncs production environment data to staging, handling both OCI file storage
52
+ and MongoDB database. Performs full MongoDB database dumps and restores.
53
+
54
+ MongoDB Backup Strategy:
55
+ - Full database dump with all collections (excluding configured collections)
56
+ - Restores to staging database with --drop flag
57
+
58
+ OPTIONS
59
+ --backup-only Backup MongoDB only (dump without restoring to staging)
60
+ --db-only Sync only MongoDB database, skip file sync
61
+ --storage-only Sync only OCI files, skip database sync
62
+ --upload-artifacts Upload backup artifacts to OCI bucket (instead of GitHub artifacts)
63
+ --replica-set Use replica set sync mode (overrides .env setting)
64
+ --no-replica-set Disable replica set sync mode (overrides .env setting)
65
+ --no-cleanup Skip cleanup of old backups (older than 7 days)
66
+ -v, --verbose Enable verbose output (show detailed command execution)
67
+ -h, --help Show this help message and exit
68
+
69
+ EXAMPLES
70
+ # Sync both files and database (default behavior)
71
+ env-sync
72
+
73
+ # Sync only database
74
+ env-sync --db-only
75
+
76
+ # Sync files only
77
+ env-sync --storage-only
78
+
79
+ # Sync with replica set mode
80
+ env-sync --replica-set
81
+
82
+ # Upload backup artifacts to OCI bucket (for CI/CD)
83
+ env-sync --upload-artifacts
84
+
85
+ # Backup database only (no restore to staging, no storage sync)
86
+ env-sync --backup-only
87
+
88
+ # Backup and upload artifacts to OCI bucket
89
+ env-sync --backup-only --upload-artifacts
90
+
91
+ # Or combine with sync operations
92
+ env-sync --db-only --upload-artifacts
93
+
94
+ CONFIGURATION
95
+ The script reads configuration from .env file in the script directory.
96
+ Required variables:
97
+ - MongoDB: PROD_HOST, PROD_USERNAME, PROD_PASSWORD, PROD_AUTH_DATABASE, PROD_DB
98
+ STAGING_HOST, STAGING_USERNAME, STAGING_PASSWORD, STAGING_AUTH_DATABASE, STAGING_DB
99
+ - OCI/Rclone: STORAGE_REMOTE_NAME, STORAGE_TYPE, OCI_COMPARTMENT, OCI_NAMESPACE, OCI_REGION
100
+ OCI_USER, OCI_FINGERPRINT, OCI_KEY_FILE, OCI_TENANCY
101
+ - Paths: STORAGE_SOURCE, STORAGE_DEST
102
+ - Artifacts: ARTIFACTS_BUCKET (required for --upload-artifacts)
103
+ ARTIFACTS_PREFIX (optional, default: "env-sync")
104
+ - Optional: EXCLUDE_COLLECTIONS (space-separated list of collections to exclude)
105
+ BACKUP_RETENTION_DAYS (default: 7)
106
+
107
+ BACKUP METADATA
108
+ Backup metadata is stored in backups/.backup_metadata and tracks:
109
+ - Last backup timestamp
110
+ `);
111
+ }
@@ -0,0 +1,37 @@
1
+ export interface MongoConfig {
2
+ authDatabase: string;
3
+ database: string;
4
+ host: string;
5
+ password: string;
6
+ username: string;
7
+ }
8
+ export interface StorageConfig {
9
+ compartment: string;
10
+ dest: string;
11
+ fingerprint: string;
12
+ keyFile: string;
13
+ namespace: string;
14
+ region: string;
15
+ remoteName: string;
16
+ source: string;
17
+ tenancy: string;
18
+ type: string;
19
+ user: string;
20
+ }
21
+ export interface ArtifactsConfig {
22
+ /** OCI bucket name for storing artifacts */
23
+ bucket: string;
24
+ /** Optional prefix/folder within the bucket */
25
+ prefix: string;
26
+ }
27
+ export interface SyncConfig {
28
+ artifacts: ArtifactsConfig;
29
+ backupDir: string;
30
+ backupRetentionDays: number;
31
+ databaseProduction: MongoConfig;
32
+ databaseStaging: MongoConfig;
33
+ excludeCollections: string[];
34
+ scriptDir: string;
35
+ storage: StorageConfig;
36
+ }
37
+ export declare function loadConfig(): SyncConfig;
@@ -0,0 +1,91 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ function parseEnvValue(value, defaultValue = '') {
5
+ return value?.trim() || defaultValue;
6
+ }
7
+ function parseEnvNumber(value, defaultValue) {
8
+ if (!value)
9
+ return defaultValue;
10
+ const parsed = parseInt(value.trim(), 10);
11
+ return isNaN(parsed) ? defaultValue : parsed;
12
+ }
13
+ export function loadConfig() {
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+ const scriptDir = path.resolve(__dirname, '../..');
17
+ const envFile = path.join(scriptDir, '.env');
18
+ if (!existsSync(envFile)) {
19
+ throw new Error(`Environment file not found at ${envFile}\nPlease copy env.example to .env and configure it`);
20
+ }
21
+ // Load .env file manually (dotenv doesn't work well with ESM in this context)
22
+ const envContent = readFileSync(envFile, 'utf-8');
23
+ const envVars = {};
24
+ for (const line of envContent.split('\n')) {
25
+ const trimmed = line.trim();
26
+ if (!trimmed || trimmed.startsWith('#'))
27
+ continue;
28
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
29
+ if (match) {
30
+ const key = match[1].trim();
31
+ const value = match[2].trim().replace(/^["']|["']$/g, '');
32
+ envVars[key] = value;
33
+ }
34
+ }
35
+ // Validate required MongoDB variables
36
+ const requiredMongoVars = ['PROD_HOST', 'STAGING_HOST'];
37
+ for (const varName of requiredMongoVars) {
38
+ if (!envVars[varName]) {
39
+ throw new Error(`MongoDB configuration missing: ${varName} is required in ${envFile}`);
40
+ }
41
+ }
42
+ // Validate required Storage variables
43
+ const requiredStorageVars = ['STORAGE_SOURCE', 'STORAGE_DEST', 'STORAGE_REMOTE_NAME', 'STORAGE_TYPE', 'OCI_COMPARTMENT', 'OCI_FINGERPRINT', 'OCI_KEY_FILE', 'OCI_NAMESPACE', 'OCI_REGION', 'OCI_TENANCY', 'OCI_USER'];
44
+ for (const varName of requiredStorageVars) {
45
+ if (!envVars[varName]) {
46
+ throw new Error(`Storage configuration missing: ${varName} is required in ${envFile}`);
47
+ }
48
+ }
49
+ const backupDir = path.join(scriptDir, 'backups');
50
+ return {
51
+ artifacts: {
52
+ bucket: parseEnvValue(envVars.ARTIFACTS_BUCKET, ''),
53
+ prefix: parseEnvValue(envVars.ARTIFACTS_PREFIX, ''),
54
+ },
55
+ backupDir,
56
+ backupRetentionDays: parseEnvNumber(envVars.BACKUP_RETENTION_DAYS, 7),
57
+ databaseProduction: {
58
+ authDatabase: parseEnvValue(envVars.PROD_AUTH_DATABASE, 'admin'),
59
+ database: parseEnvValue(envVars.PROD_DB),
60
+ host: parseEnvValue(envVars.PROD_HOST),
61
+ password: parseEnvValue(envVars.PROD_PASSWORD),
62
+ username: parseEnvValue(envVars.PROD_USERNAME),
63
+ },
64
+ databaseStaging: {
65
+ authDatabase: parseEnvValue(envVars.STAGING_AUTH_DATABASE, 'admin'),
66
+ database: parseEnvValue(envVars.STAGING_DB),
67
+ host: parseEnvValue(envVars.STAGING_HOST),
68
+ password: parseEnvValue(envVars.STAGING_PASSWORD),
69
+ username: parseEnvValue(envVars.STAGING_USERNAME),
70
+ },
71
+ excludeCollections: parseEnvValue(envVars.EXCLUDE_COLLECTIONS, '')
72
+ .split(/\s+/)
73
+ .filter(c => c.length > 0),
74
+ scriptDir,
75
+ storage: {
76
+ //
77
+ dest: parseEnvValue(envVars.STORAGE_DEST),
78
+ remoteName: parseEnvValue(envVars.STORAGE_REMOTE_NAME),
79
+ source: parseEnvValue(envVars.STORAGE_SOURCE),
80
+ type: parseEnvValue(envVars.STORAGE_TYPE),
81
+ //
82
+ compartment: parseEnvValue(envVars.OCI_COMPARTMENT),
83
+ fingerprint: parseEnvValue(envVars.OCI_FINGERPRINT),
84
+ keyFile: parseEnvValue(envVars.OCI_KEY_FILE),
85
+ namespace: parseEnvValue(envVars.OCI_NAMESPACE),
86
+ region: parseEnvValue(envVars.OCI_REGION),
87
+ tenancy: parseEnvValue(envVars.OCI_TENANCY),
88
+ user: parseEnvValue(envVars.OCI_USER),
89
+ },
90
+ };
91
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare const renderTitle: () => void;