or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asset-management.mdconfiguration.mdgithub-integration.mdindex.mdplugin-lifecycle.mdutilities.md

asset-management.mddocs/

0

# Asset Management

1

2

File asset handling system supporting glob patterns, metadata, and upload management for GitHub releases. The asset management system provides flexible file selection with powerful globbing capabilities and customizable metadata.

3

4

## Capabilities

5

6

### Asset Globbing

7

8

Resolves glob patterns and file paths into concrete asset objects ready for upload to GitHub releases.

9

10

```javascript { .api }

11

/**

12

* Resolves glob patterns for asset files

13

* @param context - Context object containing current working directory

14

* @param assets - Array of asset configurations with paths/globs

15

* @returns Promise resolving to array of concrete asset file paths

16

*/

17

async function globAssets(

18

context: { cwd: string },

19

assets: AssetConfig[]

20

): Promise<ResolvedAsset[]>;

21

22

type AssetConfig = string | {

23

path: string | string[];

24

name?: string;

25

label?: string;

26

};

27

28

interface ResolvedAsset {

29

path: string;

30

name?: string;

31

label?: string;

32

}

33

```

34

35

**Glob Pattern Support:**

36

- Standard glob patterns (`*.js`, `**/*.json`)

37

- Multiple patterns per asset (`["dist/*.js", "docs/*.pdf"]`)

38

- Negation patterns (`!**/*.test.js`)

39

- Directory expansion with automatic recursion

40

- Unique file deduplication across patterns

41

42

**Usage Examples:**

43

44

```javascript

45

import globAssets from "@semantic-release/github/lib/glob-assets.js";

46

47

// Simple glob patterns

48

const assets1 = await globAssets(

49

{ cwd: "/project" },

50

[

51

"dist/*.js",

52

"docs/*.pdf",

53

"build/assets/*.css"

54

]

55

);

56

// Returns: [{ path: "dist/app.js" }, { path: "docs/guide.pdf" }, ...]

57

58

// Complex asset configurations

59

const assets2 = await globAssets(

60

{ cwd: "/project" },

61

[

62

{

63

path: ["dist/*.js", "dist/*.js.map"],

64

label: "JavaScript Distribution"

65

},

66

{

67

path: "docs/**/*.pdf",

68

name: "documentation-${nextRelease.version}.pdf", // Template support

69

label: "Documentation Bundle"

70

}

71

]

72

);

73

74

// Mixed string and object configurations

75

const assets3 = await globAssets(

76

{ cwd: "/project" },

77

[

78

"README.md", // Simple file

79

"dist/*.zip", // Glob pattern

80

{

81

path: "build/**/*.{js,css}", // Complex glob with metadata

82

label: "Built Assets"

83

}

84

]

85

);

86

```

87

88

### Asset Configuration Types

89

90

Different ways to configure assets for release uploads.

91

92

```javascript { .api }

93

// String configuration - simple file path or glob

94

type StringAsset = string;

95

96

// Object configuration - path with metadata

97

interface ObjectAsset {

98

/** File path or glob pattern(s) */

99

path: string | string[];

100

101

/** Custom filename for uploaded asset (supports templates) */

102

name?: string;

103

104

/** Human-readable label shown in GitHub release UI */

105

label?: string;

106

}

107

108

// Combined asset configuration type

109

type AssetConfig = StringAsset | ObjectAsset;

110

```

111

112

**Configuration Examples:**

113

114

```javascript

115

// String configurations

116

const stringAssets = [

117

"dist/app.js", // Single file

118

"build/*.zip", // Glob pattern

119

"docs/api.pdf" // Documentation file

120

];

121

122

// Object configurations with metadata

123

const objectAssets = [

124

{

125

path: "dist/app.min.js",

126

name: "app-${nextRelease.version}.min.js",

127

label: "Minified Application Bundle"

128

},

129

{

130

path: ["src/**/*.ts", "!src/**/*.test.ts"],

131

name: "source-code.zip",

132

label: "TypeScript Source Code"

133

},

134

{

135

path: "coverage/lcov-report/**/*",

136

label: "Test Coverage Report"

137

}

138

];

139

140

// Mixed configurations

141

const mixedAssets = [

142

"LICENSE", // Simple file

143

"CHANGELOG.md", // Another simple file

144

{

145

path: "dist/*.{js,css,map}",

146

label: "Distribution Files"

147

}

148

];

149

```

150

151

### Asset Processing Pipeline

152

153

The asset processing pipeline handles glob resolution, file validation, and metadata preparation.

154

155

```javascript { .api }

156

/**

157

* Asset processing pipeline stages:

158

* 1. Glob expansion - Convert patterns to file lists

159

* 2. Directory handling - Expand directories to files

160

* 3. File validation - Check existence and accessibility

161

* 4. Deduplication - Remove duplicate file paths

162

* 5. Metadata preparation - Apply names and labels

163

* 6. Template processing - Resolve template variables

164

*/

165

166

interface ProcessingStages {

167

/** Expand glob patterns using globby library */

168

expandGlobs(patterns: string[], options: GlobOptions): Promise<string[]>;

169

170

/** Validate files exist and are readable */

171

validateFiles(paths: string[]): Promise<string[]>;

172

173

/** Remove duplicate file paths */

174

deduplicateFiles(paths: string[]): string[];

175

176

/** Apply asset metadata and templates */

177

prepareMetadata(assets: AssetConfig[], files: string[]): ResolvedAsset[];

178

}

179

```

180

181

**Processing Example:**

182

183

```javascript

184

// Input configuration

185

const assetConfig = {

186

path: ["dist/**/*.js", "!dist/**/*.test.js"],

187

name: "${basename}-${nextRelease.version}${extname}",

188

label: "JavaScript Distribution"

189

};

190

191

// Processing stages:

192

// 1. Glob expansion: ["dist/app.js", "dist/utils.js", "dist/app.test.js"]

193

// 2. Negation filtering: ["dist/app.js", "dist/utils.js"]

194

// 3. File validation: Check files exist and are readable

195

// 4. Metadata application: Add name templates and labels

196

// 5. Result: [

197

// { path: "dist/app.js", name: "app-1.0.0.js", label: "JavaScript Distribution" },

198

// { path: "dist/utils.js", name: "utils-1.0.0.js", label: "JavaScript Distribution" }

199

// ]

200

```

201

202

### Upload Process

203

204

Asset upload handling within the publish lifecycle function.

205

206

```javascript { .api }

207

/**

208

* Asset upload process during release publication:

209

* 1. Create draft release

210

* 2. Resolve asset globs

211

* 3. Upload assets in parallel

212

* 4. Publish release (if not draft mode)

213

*/

214

215

interface UploadProcess {

216

/** File stat checking and validation */

217

validateAssetFile(filePath: string): Promise<FileStats>;

218

219

/** MIME type detection for proper Content-Type headers */

220

detectMimeType(filePath: string): string;

221

222

/** Asset upload with retry logic */

223

uploadAsset(asset: ResolvedAsset, uploadUrl: string): Promise<AssetResponse>;

224

225

/** Parallel upload coordination */

226

uploadAllAssets(assets: ResolvedAsset[]): Promise<AssetResponse[]>;

227

}

228

229

interface FileStats {

230

size: number;

231

isFile: boolean;

232

isDirectory: boolean;

233

}

234

235

interface AssetResponse {

236

id: number;

237

name: string;

238

size: number;

239

download_count: number;

240

browser_download_url: string;

241

}

242

```

243

244

**Upload Error Handling:**

245

246

```javascript

247

// The upload process includes comprehensive error handling:

248

249

try {

250

await uploadAsset(asset, uploadUrl);

251

} catch (error) {

252

if (error.code === 'ENOENT') {

253

logger.error(`Asset file not found: ${asset.path}`);

254

// File is skipped, upload continues

255

} else if (error.status === 422) {

256

logger.error(`Asset already exists: ${asset.name}`);

257

// GitHub rejects duplicate asset names

258

} else if (error.status === 413) {

259

logger.error(`Asset too large: ${asset.path}`);

260

// GitHub has file size limits

261

} else {

262

// Other errors are retried or escalated

263

throw error;

264

}

265

}

266

```

267

268

### Template Support

269

270

Asset names and labels support Lodash templates for dynamic content generation.

271

272

```javascript { .api }

273

// Available template variables in asset configurations:

274

interface AssetTemplateContext {

275

nextRelease: {

276

version: string; // "1.0.0"

277

gitTag: string; // "v1.0.0"

278

name: string; // "1.0.0"

279

channel?: string; // Release channel

280

};

281

branch: {

282

name: string; // "main"

283

};

284

// File path utilities (for name templates)

285

basename: string; // "app.js" from "dist/app.js"

286

extname: string; // ".js" from "dist/app.js"

287

dirname: string; // "dist" from "dist/app.js"

288

filename: string; // "app" from "dist/app.js"

289

}

290

```

291

292

**Template Examples:**

293

294

```javascript

295

{

296

// Version-based naming

297

name: "${basename}-v${nextRelease.version}${extname}",

298

// "app.js" becomes "app-v1.0.0.js"

299

300

// Channel-aware naming

301

name: "${filename}-${nextRelease.channel || 'stable'}${extname}",

302

// "app.js" becomes "app-stable.js" or "app-beta.js"

303

304

// Complex naming with metadata

305

name: "${dirname}-${filename}-${nextRelease.version}-${branch.name}${extname}",

306

// "dist/app.js" becomes "dist-app-1.0.0-main.js"

307

308

// Dynamic labels

309

label: "${basename} (${nextRelease.version}${nextRelease.channel ? ` - ${nextRelease.channel}` : ''})"

310

// "App Bundle (1.0.0)" or "App Bundle (1.0.0 - beta)"

311

}

312

```

313

314

### Advanced Patterns

315

316

Complex asset management patterns for sophisticated release workflows.

317

318

**Conditional Assets:**

319

```javascript

320

// Assets based on build configuration

321

const assets = [

322

// Always include core files

323

"LICENSE",

324

"README.md",

325

326

// Conditional inclusion based on environment

327

...(process.env.NODE_ENV === 'production' ? [

328

"dist/app.min.js",

329

"dist/app.min.css"

330

] : [

331

"dist/app.js",

332

"dist/app.css"

333

]),

334

335

// Platform-specific assets

336

{

337

path: "build/darwin/**/*",

338

label: "macOS Distribution"

339

},

340

{

341

path: "build/win32/**/*",

342

label: "Windows Distribution"

343

}

344

];

345

```

346

347

**Multi-Architecture Builds:**

348

```javascript

349

const architectures = ['x64', 'arm64', 'x86'];

350

351

const assets = architectures.flatMap(arch => [

352

{

353

path: `dist/${arch}/app`,

354

name: `app-${arch}-${nextRelease.version}`,

355

label: `Application (${arch.toUpperCase()})`

356

},

357

{

358

path: `dist/${arch}/app.exe`,

359

name: `app-${arch}-${nextRelease.version}.exe`,

360

label: `Application (${arch.toUpperCase()} Windows)`

361

}

362

]);

363

```

364

365

**Documentation Bundles:**

366

```javascript

367

const assets = [

368

// Individual documentation files

369

{

370

path: "docs/**/*.md",

371

label: "Individual Documentation Files"

372

},

373

374

// Generated documentation bundle

375

{

376

path: "docs-build/api-docs.zip",

377

name: "api-documentation-${nextRelease.version}.zip",

378

label: "Complete API Documentation"

379

},

380

381

// Coverage reports

382

{

383

path: "coverage/lcov.info",

384

name: "test-coverage-${nextRelease.version}.lcov",

385

label: "Test Coverage Data"

386

}

387

];

388

```

389

390

### Performance Considerations

391

392

Asset management includes several performance optimizations:

393

394

- **Parallel Uploads**: Multiple assets uploaded simultaneously

395

- **Glob Caching**: File system scans cached within single run

396

- **File Streaming**: Large files streamed rather than loaded into memory

397

- **Resume Support**: Failed uploads can be resumed (GitHub API dependent)

398

- **Progress Reporting**: Upload progress logged for large files

399

- **Size Validation**: File size checked before upload attempt