Skip to content

Commit e0bb8d6

Browse files
Add command to download files (#1220)
* feat: add command to download files * feat: add context folder to dcl ignore * feat: add valid scene validation and posinstall package json * feat: remove postinstall * feat: add postinstall on sdk commands * feat:postinstall only on installing package * feat: change postinstall script * feat: add context to .dclignore * feat: add node modules check * feat: separate in function and update postinstall * feat: add cli errors, add command comment, send all components * feat: add recursive to check all files and remove dcl ignore adding * feat: fix package json * feat: add script to run the commands * feat: package json update * feat: improve logs, remove folder on start running * feat: add postinstall script * feat: add correct postinstall script and gitignor * feat: update git ignore and postinstall logs
1 parent 1bcaa4a commit e0bb8d6

11 files changed

Lines changed: 182 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ test/snapshots/bin
3333
**/.decentraland/ts-entry-points
3434

3535
test/build-ecs/fixtures/ecs7-scene/aCaseSensitiveReadme.md
36+
!packages/@dcl/sdk-commands/scripts/*.js

packages/@dcl/sdk-commands/package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@dcl/sdk-commands/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353
},
5454
"files": [
5555
"dist",
56-
".dclrc"
56+
".dclrc",
57+
"scripts"
5758
],
5859
"keywords": [],
5960
"license": "Apache-2.0",
@@ -64,7 +65,8 @@
6465
},
6566
"scripts": {
6667
"build": "tsc -p tsconfig.json && rm -rf dist/locales && cp -r src/locales dist/locales",
67-
"start": "tsc -p tsconfig.json --watch && rm -rf dist/locales && cp -r src/locales dist/locales"
68+
"start": "tsc -p tsconfig.json --watch && rm -rf dist/locales && cp -r src/locales dist/locales",
69+
"postinstall": "node scripts/postinstall.js"
6870
},
6971
"tsdoc": {
7072
"tsdocFlavor": "AEDoc"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env node
2+
3+
if (process.cwd().includes('node_modules')) {
4+
const { exec } = require('child_process')
5+
6+
//exec script on the root scene
7+
const scenePath = process.cwd().split('/node_modules')[0]
8+
exec('npx @dcl/sdk-commands get-context-files', { cwd: scenePath }, (e, stdout, stderr) => {
9+
if (e) {
10+
console.log('Error: not able to execute get-context-files', stderr)
11+
} else {
12+
console.log(stdout)
13+
}
14+
})
15+
} else {
16+
console.log('Not executing postinstall from this directory')
17+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import path from 'path'
2+
import { CliComponents } from '../../components'
3+
import { declareArgs } from '../../logic/args'
4+
import { assertValidProjectFolder } from '../../logic/project-validations'
5+
import { CliError } from '../../logic/error'
6+
import i18next from 'i18next'
7+
8+
import { Result } from 'arg'
9+
const GITHUB_API_BASE = 'https://api.github.com/repos/decentraland/documentation/contents/ai-sdk-context'
10+
interface GitHubFile {
11+
name: string
12+
path: string
13+
type: 'file' | 'dir'
14+
download_url?: string
15+
url: string
16+
}
17+
18+
export interface Options {
19+
args: Result<typeof args>
20+
components: Pick<CliComponents, 'logger' | 'fs' | 'fetch'>
21+
}
22+
23+
export const args = declareArgs({
24+
'--help': Boolean,
25+
'-h': '--help'
26+
})
27+
28+
export function help(options: Options) {
29+
options.components.logger.log(`
30+
Usage: 'sdk-commands get-context-files [options]'
31+
Options:
32+
-h, --help Gets & updates context files from https://github.com/decentraland/documentation/tree/main/ai-sdk-context only on valid scenes
33+
34+
Example:
35+
- Get context files:
36+
$ sdk-commands get-context-files
37+
`)
38+
}
39+
40+
async function listFilesFromPath(components: Pick<CliComponents, 'fetch'>, url: string): Promise<GitHubFile[]> {
41+
const response = await components.fetch.fetch(url)
42+
43+
if (!response.ok) {
44+
throw new CliError('GET_CONTEXT_FILES_LIST_FAILED', i18next.t('errors.get_context_files.list_failed'))
45+
}
46+
47+
return (await response.json()) as GitHubFile[]
48+
}
49+
50+
async function downloadFile(components: Pick<CliComponents, 'fetch'>, url: string): Promise<string> {
51+
const response = await components.fetch.fetch(url)
52+
if (!response.ok) {
53+
throw new CliError('GET_CONTEXT_FILES_DOWNLOAD_FAILED', i18next.t('errors.get_context_files.download_failed'))
54+
}
55+
return await response.text()
56+
}
57+
58+
async function getAllFiles(
59+
components: Pick<CliComponents, 'fetch'>,
60+
initialUrl: string = GITHUB_API_BASE
61+
): Promise<Array<{ url: string; filename: string; path: string }>> {
62+
const files: Array<{ url: string; filename: string; path: string }> = []
63+
const rootFiles = await listFilesFromPath(components, initialUrl)
64+
for (const file of rootFiles) {
65+
if (file.type === 'file') {
66+
if (file.download_url) {
67+
const filename = file.name
68+
const fileInfo = {
69+
url: file.download_url,
70+
filename: filename,
71+
path: file.path
72+
}
73+
files.push(fileInfo)
74+
}
75+
} else if (file.type === 'dir') {
76+
const subFiles = await getAllFiles(components, file.url)
77+
files.push(...subFiles)
78+
}
79+
}
80+
81+
return files
82+
}
83+
84+
export async function main(options: Options) {
85+
const targetDir = process.cwd()
86+
try {
87+
await assertValidProjectFolder(options.components, targetDir)
88+
options.components.logger.log('✓ Valid Scene project')
89+
} catch (error) {
90+
options.components.logger.log('Not a valid Scene...')
91+
return
92+
}
93+
94+
const contextDir = path.join(targetDir, 'dclcontext')
95+
const contextExists = await options.components.fs.directoryExists(contextDir)
96+
97+
if (contextExists) {
98+
options.components.logger.log('Context directory exists. Removing old files...')
99+
await options.components.fs.rm(contextDir, { recursive: true })
100+
}
101+
102+
options.components.logger.log('Creating context directory...')
103+
await options.components.fs.mkdir(contextDir)
104+
105+
const filesToDownload = await getAllFiles(options.components, GITHUB_API_BASE)
106+
const successfulDownloads: string[] = []
107+
const failedDownloads: Array<{ filePath: string; error: string }> = []
108+
109+
const downloadPromises = filesToDownload.map(async ({ url, filename, path: filePath }) => {
110+
try {
111+
const content = await downloadFile(options.components, url)
112+
const localFilePath = path.join(contextDir, filename)
113+
await options.components.fs.writeFile(localFilePath, content)
114+
options.components.logger.log(`✓ Saved ${filePath}`)
115+
successfulDownloads.push(filePath)
116+
} catch (error) {
117+
const errorMessage = error instanceof Error ? error.message : String(error)
118+
options.components.logger.log(`✗ Failed to download ${filePath}: ${errorMessage}`)
119+
failedDownloads.push({ filePath, error: errorMessage })
120+
}
121+
})
122+
123+
await Promise.all(downloadPromises)
124+
125+
options.components.logger.log(
126+
`\nDownload complete: ${successfulDownloads.length} successful, ${failedDownloads.length} failed`
127+
)
128+
if (successfulDownloads.length > 0) {
129+
options.components.logger.log(`Successfully downloaded:`)
130+
successfulDownloads.forEach((filePath) => {
131+
options.components.logger.log(` ✓ ${filePath}`)
132+
})
133+
}
134+
135+
if (failedDownloads.length > 0) {
136+
options.components.logger.log(`Failed downloads:`)
137+
failedDownloads.forEach(({ filePath, error }) => {
138+
options.components.logger.log(` ✗ ${filePath}: ${error}`)
139+
})
140+
}
141+
}

packages/@dcl/sdk-commands/src/locales/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
"invalid_realm_name": "--realmName has invalid characters",
4949
"invalid_output_directory": "The destination path {{outputDirectory}} is not a directory"
5050
},
51+
"get_context_files": {
52+
"list_failed": "Failed to list context files",
53+
"download_failed": "Failed to download context file"
54+
},
5155
"init": {
5256
"dir_not_empty": "The target directory specified is not empty. Run this command with --yes to override.",
5357
"invalid_arguments": "Specifying --template and --project at the same time is not allowed. Please specify only one of them.",

packages/@dcl/sdk-commands/src/locales/es.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
"invalid_realm_name": "--realmName tiene caracteres inválidos",
4949
"invalid_output_directory": "La ruta de destino {{outputDirectory}} no es un directorio"
5050
},
51+
"get_context_files": {
52+
"list_failed": "Error al listar archivos de contexto",
53+
"download_failed": "Error al descargar archivo de contexto"
54+
},
5155
"init": {
5256
"dir_not_empty": "El directorio de destino especificado no está vacío. Ejecuta este comando con --yes para sobrescribir.",
5357
"invalid_arguments": "No se permite especificar --template y --project al mismo tiempo. Por favor especifica solo uno de ellos.",

packages/@dcl/sdk-commands/src/locales/zh.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
"invalid_realm_name": "--realmName 包含无效字符",
4949
"invalid_output_directory": "目标路径 {{outputDirectory}} 不是一个目录"
5050
},
51+
"get_context_files": {
52+
"list_failed": "列出上下文文件失败",
53+
"download_failed": "下载上下文文件失败"
54+
},
5155
"init": {
5256
"dir_not_empty": "指定的目标目录不为空。使用 --yes 运行此命令以覆盖。",
5357
"invalid_arguments": "不允许同时指定 --template 和 --project。请只指定其中一个。",

packages/@dcl/sdk-commands/src/logic/dcl-ignore.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const defaultDclIgnore = [
1111
'tsconfig.json',
1212
'tslint.json',
1313
'node_modules',
14+
'context',
1415
'**/*.ts',
1516
'**/*.tsx',
1617
'Dockerfile',

packages/@dcl/sdk-commands/src/logic/error.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export type CliErrorName =
2626
| 'QUESTS_INVALID_UUID'
2727
// Components errors
2828
| 'CONFIG_NOT_PROVIDED'
29+
// Get context files errors
30+
| 'GET_CONTEXT_FILES_LIST_FAILED'
31+
| 'GET_CONTEXT_FILES_DOWNLOAD_FAILED'
2932
// General errors
3033
| 'ACCOUNT_INVALID_PRIVATE_KEY'
3134
| 'ARGS_ARG_ERROR'

0 commit comments

Comments
 (0)