Skip to content
This repository was archived by the owner on Apr 15, 2024. It is now read-only.

Commit bdb0e02

Browse files
committed
Add debugging logs to the Generate command, and add ability to filter filenames for Pipelines - dbt (jinja2) clashes with lodash
1 parent 720f0bf commit bdb0e02

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

src/commands/pipelines.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import debugSetup from 'debug';
22
import fs from 'fs';
33
import dayjs from 'dayjs';
4+
import path from 'path';
45
import _ from 'lodash';
56
import relativeTime from 'dayjs/plugin/relativeTime.js';
67
import { loadProfile } from '../config.js';
@@ -277,6 +278,18 @@ export const PipelineGenerateCommand = class extends TemplateGenerationCommand {
277278
super(program, 'Pipeline', 'pipelines', 'pipelinename', 'pipelineTemplateConfig');
278279
}
279280

281+
// TODO: Need a better way to handle this filtering
282+
filterByFileName(filepath) {
283+
// NOTE: It is common for dbt's templating (jinja2?) to collide with Lodash's templating syntax.
284+
// Current workaround is to exclude DBT & SQL files from templating, only downside is that those
285+
// can't use the {{pipelinename}} variable.
286+
const fileName = path.posix.basename(filepath);
287+
if (path.extname(fileName) === '.sql' || filepath.includes('dbt_packages') || filepath.includes('/dbt/')) {
288+
return true;
289+
}
290+
return false;
291+
}
292+
280293
async configureSubcommand() {
281294
await (new PipelineTemplateConfigureCommand(this.program)).execute({ refresh: true });
282295
}

src/commands/workspaces/generate.js

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ export class TemplateGenerationCommand {
6969
if (typeof this.configureSubcommand !== 'function') {
7070
throw new TypeError('Cannot construct instance of TemplateGenerationCommand without overriding async method "configureSubcommand()"!');
7171
}
72+
if (typeof this.filterByFileName !== 'function') {
73+
throw new TypeError('Cannot construct instance of TemplateGenerationCommand without overriding async method "filterByFileName(string) -> bool"!');
74+
}
7275
// Set options controlling direct behavior
7376
this.program = program;
7477
this.resourceName = resourceName;
@@ -158,7 +161,9 @@ export class TemplateGenerationCommand {
158161
globTree(tree, glob) {
159162
debug('Checking for templates that include the glob pattern: %s', glob);
160163
const filterFunc = minimatch.filter(glob, { matchBase: true });
161-
return _.filter(tree, (f) => f.type === 'blob' && filterFunc(f.path));
164+
const res = _.filter(tree, (f) => f.type === 'blob' && filterFunc(f.path));
165+
debug('Number of results matching glob pattern "%s": %s', glob, res.length);
166+
return res;
162167
}
163168

164169
/**
@@ -236,6 +241,7 @@ export class TemplateGenerationCommand {
236241
const loadMetadata = async (value) => {
237242
// TODO: this should have error handling to avoid a bad template crashing the CLI
238243
const data = JSON.parse((await this.readFile(value.path)).toString());
244+
debug('Successfully read metadata file: %s', value.path);
239245
return {
240246
name: data.title,
241247
value: { ...data, path: value.path },
@@ -299,13 +305,44 @@ export class TemplateGenerationCommand {
299305
[this.resourceTemplateName]: generateNameFromTitle(templateName),
300306
});
301307
} catch (err) {
302-
printError(err.message, this.options);
308+
this.handleTemplatingError(templateName, f, err);
303309
}
304310
return undefined;
305311
}).join('<br>');
306312
return generatedFiles;
307313
}
308314

315+
/**
316+
* Error handler for templating specific scenarios - exits the program.
317+
*
318+
* @param {string} templateName Name of the template being copied
319+
* @param {GitTreeWrapper} file File object that was being templated
320+
* @param {Error} err Error that was thrown
321+
*/
322+
handleTemplatingError(templateName, file, err) {
323+
debug('Template Name: %s', templateName);
324+
debug('File details: %s', file);
325+
debug('Error details: %s', err);
326+
const message = `Failed to generate file "${file.path}" in template "${templateName}"!\n`
327+
+ `This is possibly the result of the ${this.resourceName} template having an invalid syntax.\n`
328+
+ `\nError: ${err.message}`;
329+
printError(message, this.options);
330+
}
331+
332+
/**
333+
* Returns whether a given file (path) in the template should be excluded from the template.
334+
* Should return `true` if the file should be copied without modification.
335+
*
336+
* The default implementation allows for all files to be templated.
337+
*
338+
* @param {string} filepath
339+
* @returns {boolean} true if the file should NOT be templated, false otherwise
340+
*/
341+
// eslint-disable-next-line no-unused-vars
342+
filterByFileName(filepath) {
343+
return false;
344+
}
345+
309346
/**
310347
* Generates the selected template by templating its contents and writing its content to disk.
311348
* Returns the file tree with what files were created.
@@ -328,8 +365,8 @@ export class TemplateGenerationCommand {
328365
template: template.template,
329366
};
330367
let buf = await this.readFile(f.path);
331-
/// Try not to template any non-text files.
332-
if (isText(null, buf)) {
368+
/// Try not to template any non-text files, and allow for a filter
369+
if (isText(null, buf) && !this.filterByFileName(f.path)) {
333370
buf = Buffer.from(_.template(buf.toString(), { interpolate: /{{([\s\S]+?)}}/g })(templateVars));
334371
}
335372
const relPath = f.path.slice(path.dirname(template.template.path).length);
@@ -340,7 +377,7 @@ export class TemplateGenerationCommand {
340377
_.set(treeObj, sourcePath.split('/'), null);
341378
}
342379
} catch (err) {
343-
printError(err.message, this.options);
380+
this.handleTemplatingError(template.name, f, err);
344381
}
345382
}));
346383
return treeObj;

0 commit comments

Comments
 (0)