skuba 8.1.0-pm-20240430084912 → 8.1.0-skuba-dive-secret-20240522031929

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.
@@ -9,3 +9,4 @@ export { getOwnerAndRepo } from './remote';
9
9
  export { push } from './push';
10
10
  export { fastForwardBranch } from './pull';
11
11
  export { reset } from './reset';
12
+ export { isFileGitIgnored } from './isFileGitIgnored';
@@ -27,6 +27,7 @@ __export(git_exports, {
27
27
  getHeadCommitId: () => import_log.getHeadCommitId,
28
28
  getHeadCommitMessage: () => import_log.getHeadCommitMessage,
29
29
  getOwnerAndRepo: () => import_remote.getOwnerAndRepo,
30
+ isFileGitIgnored: () => import_isFileGitIgnored.isFileGitIgnored,
30
31
  push: () => import_push.push,
31
32
  reset: () => import_reset.reset
32
33
  });
@@ -41,6 +42,7 @@ var import_remote = require("./remote");
41
42
  var import_push = require("./push");
42
43
  var import_pull = require("./pull");
43
44
  var import_reset = require("./reset");
45
+ var import_isFileGitIgnored = require("./isFileGitIgnored");
44
46
  // Annotate the CommonJS export names for ESM import in node:
45
47
  0 && (module.exports = {
46
48
  commit,
@@ -52,6 +54,7 @@ var import_reset = require("./reset");
52
54
  getHeadCommitId,
53
55
  getHeadCommitMessage,
54
56
  getOwnerAndRepo,
57
+ isFileGitIgnored,
55
58
  push,
56
59
  reset
57
60
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/api/git/index.ts"],
4
- "sourcesContent": ["export { commit } from './commit';\nexport { commitAllChanges } from './commitAllChanges';\nexport { currentBranch } from './currentBranch';\nexport { findRoot } from './findRoot';\nexport type { ChangedFile } from './getChangedFiles';\nexport { getChangedFiles } from './getChangedFiles';\nexport { getHeadCommitId, getHeadCommitMessage } from './log';\nexport { getOwnerAndRepo } from './remote';\nexport { push } from './push';\nexport { fastForwardBranch } from './pull';\nexport { reset } from './reset';\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAuB;AACvB,8BAAiC;AACjC,2BAA8B;AAC9B,sBAAyB;AAEzB,6BAAgC;AAChC,iBAAsD;AACtD,oBAAgC;AAChC,kBAAqB;AACrB,kBAAkC;AAClC,mBAAsB;",
4
+ "sourcesContent": ["export { commit } from './commit';\nexport { commitAllChanges } from './commitAllChanges';\nexport { currentBranch } from './currentBranch';\nexport { findRoot } from './findRoot';\nexport type { ChangedFile } from './getChangedFiles';\nexport { getChangedFiles } from './getChangedFiles';\nexport { getHeadCommitId, getHeadCommitMessage } from './log';\nexport { getOwnerAndRepo } from './remote';\nexport { push } from './push';\nexport { fastForwardBranch } from './pull';\nexport { reset } from './reset';\nexport { isFileGitIgnored } from './isFileGitIgnored';\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAuB;AACvB,8BAAiC;AACjC,2BAA8B;AAC9B,sBAAyB;AAEzB,6BAAgC;AAChC,iBAAsD;AACtD,oBAAgC;AAChC,kBAAqB;AACrB,kBAAkC;AAClC,mBAAsB;AACtB,8BAAiC;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,4 @@
1
+ export declare const isFileGitIgnored: ({ absolutePath, gitRoot, }: {
2
+ absolutePath: string;
3
+ gitRoot: string;
4
+ }) => Promise<boolean>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var isFileGitIgnored_exports = {};
30
+ __export(isFileGitIgnored_exports, {
31
+ isFileGitIgnored: () => isFileGitIgnored
32
+ });
33
+ module.exports = __toCommonJS(isFileGitIgnored_exports);
34
+ var import_path = __toESM(require("path"));
35
+ var import_fs_extra = __toESM(require("fs-extra"));
36
+ var import_isomorphic_git = __toESM(require("isomorphic-git"));
37
+ const isFileGitIgnored = ({
38
+ absolutePath,
39
+ gitRoot
40
+ }) => import_isomorphic_git.default.isIgnored({
41
+ dir: gitRoot,
42
+ filepath: import_path.default.relative(gitRoot, absolutePath),
43
+ fs: import_fs_extra.default
44
+ });
45
+ // Annotate the CommonJS export names for ESM import in node:
46
+ 0 && (module.exports = {
47
+ isFileGitIgnored
48
+ });
49
+ //# sourceMappingURL=isFileGitIgnored.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/api/git/isFileGitIgnored.ts"],
4
+ "sourcesContent": ["import path from 'path';\n\nimport fs from 'fs-extra';\nimport git from 'isomorphic-git';\n\nexport const isFileGitIgnored = ({\n absolutePath,\n gitRoot,\n}: {\n absolutePath: string;\n gitRoot: string;\n}): Promise<boolean> =>\n git.isIgnored({\n dir: gitRoot,\n filepath: path.relative(gitRoot, absolutePath),\n fs,\n });\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAiB;AAEjB,sBAAe;AACf,4BAAgB;AAET,MAAM,mBAAmB,CAAC;AAAA,EAC/B;AAAA,EACA;AACF,MAIE,sBAAAA,QAAI,UAAU;AAAA,EACZ,KAAK;AAAA,EACL,UAAU,YAAAC,QAAK,SAAS,SAAS,YAAY;AAAA,EAC7C,oBAAAC;AACF,CAAC;",
6
+ "names": ["git", "path", "fs"]
7
+ }
@@ -37,6 +37,7 @@ var import_path = __toESM(require("path"));
37
37
  var import_util = require("util");
38
38
  var import_fs_extra = require("fs-extra");
39
39
  var import_strip_ansi = __toESM(require("strip-ansi"));
40
+ var import__ = require("../../..");
40
41
  var import_npmrc = require("../../../utils/npmrc");
41
42
  var import_packageManager = require("../../../utils/packageManager");
42
43
  var import_template = require("../../../utils/template");
@@ -72,7 +73,10 @@ const REFRESHABLE_CONFIG_FILES = [
72
73
  }
73
74
  ];
74
75
  const refreshConfigFiles = async (mode, logger) => {
75
- const manifest = await (0, import_package.getDestinationManifest)();
76
+ const [manifest, gitRoot] = await Promise.all([
77
+ (0, import_package.getDestinationManifest)(),
78
+ import__.Git.findRoot({ dir: process.cwd() })
79
+ ]);
76
80
  const destinationRoot = import_path.default.dirname(manifest.path);
77
81
  const readDestinationFile = (0, import_project.createDestinationFileReader)(destinationRoot);
78
82
  const refreshConfigFile = async ({
@@ -84,10 +88,17 @@ const refreshConfigFiles = async (mode, logger) => {
84
88
  if (!condition(packageManager2)) {
85
89
  return { needsChange: false };
86
90
  }
87
- const [inputFile, templateFile] = await Promise.all([
91
+ const [inputFile, templateFile, isGitIgnored] = await Promise.all([
88
92
  readDestinationFile(filename),
89
- (0, import_template.readBaseTemplateFile)(`_${filename}`)
93
+ (0, import_template.readBaseTemplateFile)(`_${filename}`),
94
+ gitRoot ? import__.Git.isFileGitIgnored({
95
+ gitRoot,
96
+ absolutePath: import_path.default.join(destinationRoot, filename)
97
+ }) : false
90
98
  ]);
99
+ if (inputFile === void 0 && isGitIgnored) {
100
+ return { needsChange: false };
101
+ }
91
102
  const data = additionalMapping(
92
103
  inputFile ? (0, import_configFile.mergeWithConfigFile)(templateFile, fileType)(inputFile) : templateFile,
93
104
  packageManager2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/cli/lint/internalLints/refreshConfigFiles.ts"],
4
- "sourcesContent": ["import path from 'path';\nimport { inspect } from 'util';\n\nimport { writeFile } from 'fs-extra';\nimport stripAnsi from 'strip-ansi';\n\nimport type { Logger } from '../../../utils/logging';\nimport { NPMRC_LINES, hasNpmrcSecret } from '../../../utils/npmrc';\nimport {\n type PackageManagerConfig,\n detectPackageManager,\n} from '../../../utils/packageManager';\nimport { readBaseTemplateFile } from '../../../utils/template';\nimport { getDestinationManifest } from '../../configure/analysis/package';\nimport { createDestinationFileReader } from '../../configure/analysis/project';\nimport { mergeWithConfigFile } from '../../configure/processing/configFile';\nimport type { InternalLintResult } from '../internal';\n\nconst ensureNoAuthToken = (fileContents: string) =>\n fileContents\n .split('\\n')\n .filter((line) => !hasNpmrcSecret(line))\n .join('\\n');\n\ntype RefreshableConfigFile = {\n name: string;\n type: 'ignore' | 'npmrc';\n additionalMapping?: (\n s: string,\n packageManager: PackageManagerConfig,\n ) => string;\n if?: (packageManager: PackageManagerConfig) => boolean;\n};\n\nconst removeRedundantNpmrc = (contents: string) => {\n const npmrcLines = contents\n .split('\\n')\n .filter((line) => NPMRC_LINES.includes(line.trim()));\n\n // If we're only left with !.npmrc line we can remove it\n // TODO: Consider if we should generalise this\n if (npmrcLines.length > 0 && npmrcLines.every((line) => line.includes('!'))) {\n return contents\n .split('\\n')\n .filter((line) => !NPMRC_LINES.includes(line.trim()))\n .join('\\n');\n }\n return contents;\n};\n\nexport const REFRESHABLE_CONFIG_FILES: RefreshableConfigFile[] = [\n { name: '.eslintignore', type: 'ignore' },\n {\n name: '.gitignore',\n type: 'ignore',\n additionalMapping: removeRedundantNpmrc,\n },\n { name: '.prettierignore', type: 'ignore' },\n {\n name: '.npmrc',\n type: 'npmrc',\n additionalMapping: ensureNoAuthToken,\n if: (packageManager: PackageManagerConfig) =>\n packageManager.command === 'pnpm',\n },\n {\n name: '.dockerignore',\n type: 'ignore',\n additionalMapping: removeRedundantNpmrc,\n },\n];\n\nexport const refreshConfigFiles = async (\n mode: 'format' | 'lint',\n logger: Logger,\n) => {\n const manifest = await getDestinationManifest();\n\n const destinationRoot = path.dirname(manifest.path);\n\n const readDestinationFile = createDestinationFileReader(destinationRoot);\n\n const refreshConfigFile = async (\n {\n name: filename,\n type: fileType,\n additionalMapping = (s) => s,\n if: condition = () => true,\n }: RefreshableConfigFile,\n packageManager: PackageManagerConfig,\n ) => {\n if (!condition(packageManager)) {\n return { needsChange: false };\n }\n\n const [inputFile, templateFile] = await Promise.all([\n readDestinationFile(filename),\n readBaseTemplateFile(`_${filename}`),\n ]);\n\n const data = additionalMapping(\n inputFile\n ? mergeWithConfigFile(templateFile, fileType)(inputFile)\n : templateFile,\n packageManager,\n );\n\n const filepath = path.join(destinationRoot, filename);\n\n if (mode === 'format') {\n if (data === inputFile) {\n return { needsChange: false };\n }\n\n await writeFile(filepath, data);\n return {\n needsChange: false,\n msg: `Refreshed ${logger.bold(filename)}.`,\n filename,\n };\n }\n\n if (data !== inputFile) {\n return {\n needsChange: true,\n msg: `The ${logger.bold(\n filename,\n )} file is out of date. Run \\`${logger.bold(\n packageManager.exec,\n 'skuba',\n 'format',\n )}\\` to update it.`,\n filename,\n };\n }\n\n return { needsChange: false };\n };\n\n const packageManager = await detectPackageManager(destinationRoot);\n\n const results = await Promise.all(\n REFRESHABLE_CONFIG_FILES.map((conf) =>\n refreshConfigFile(conf, packageManager),\n ),\n );\n\n // Log after for reproducible test output ordering\n results.forEach((result) => {\n if (result.msg) {\n logger.warn(result.msg, logger.dim('refresh-config-files'));\n }\n });\n\n const anyNeedChanging = results.some(({ needsChange }) => needsChange);\n\n return {\n ok: !anyNeedChanging,\n fixable: anyNeedChanging,\n annotations: results.flatMap(({ needsChange, filename, msg }) =>\n needsChange && msg\n ? [\n {\n path: filename,\n message: stripAnsi(msg),\n },\n ]\n : [],\n ),\n };\n};\n\nexport const tryRefreshConfigFiles = async (\n mode: 'format' | 'lint',\n logger: Logger,\n): Promise<InternalLintResult> => {\n try {\n return await refreshConfigFiles(mode, logger);\n } catch (err) {\n logger.warn('Failed to refresh config files.');\n logger.subtle(inspect(err));\n\n return {\n ok: false,\n fixable: false,\n annotations: [],\n };\n }\n};\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAiB;AACjB,kBAAwB;AAExB,sBAA0B;AAC1B,wBAAsB;AAGtB,mBAA4C;AAC5C,4BAGO;AACP,sBAAqC;AACrC,qBAAuC;AACvC,qBAA4C;AAC5C,wBAAoC;AAGpC,MAAM,oBAAoB,CAAC,iBACzB,aACG,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,KAAC,6BAAe,IAAI,CAAC,EACtC,KAAK,IAAI;AAYd,MAAM,uBAAuB,CAAC,aAAqB;AACjD,QAAM,aAAa,SAChB,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,yBAAY,SAAS,KAAK,KAAK,CAAC,CAAC;AAIrD,MAAI,WAAW,SAAS,KAAK,WAAW,MAAM,CAAC,SAAS,KAAK,SAAS,GAAG,CAAC,GAAG;AAC3E,WAAO,SACJ,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,CAAC,yBAAY,SAAS,KAAK,KAAK,CAAC,CAAC,EACnD,KAAK,IAAI;AAAA,EACd;AACA,SAAO;AACT;AAEO,MAAM,2BAAoD;AAAA,EAC/D,EAAE,MAAM,iBAAiB,MAAM,SAAS;AAAA,EACxC;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,mBAAmB;AAAA,EACrB;AAAA,EACA,EAAE,MAAM,mBAAmB,MAAM,SAAS;AAAA,EAC1C;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,IAAI,CAAC,mBACH,eAAe,YAAY;AAAA,EAC/B;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,mBAAmB;AAAA,EACrB;AACF;AAEO,MAAM,qBAAqB,OAChC,MACA,WACG;AACH,QAAM,WAAW,UAAM,uCAAuB;AAE9C,QAAM,kBAAkB,YAAAA,QAAK,QAAQ,SAAS,IAAI;AAElD,QAAM,0BAAsB,4CAA4B,eAAe;AAEvE,QAAM,oBAAoB,OACxB;AAAA,IACE,MAAM;AAAA,IACN,MAAM;AAAA,IACN,oBAAoB,CAAC,MAAM;AAAA,IAC3B,IAAI,YAAY,MAAM;AAAA,EACxB,GACAC,oBACG;AACH,QAAI,CAAC,UAAUA,eAAc,GAAG;AAC9B,aAAO,EAAE,aAAa,MAAM;AAAA,IAC9B;AAEA,UAAM,CAAC,WAAW,YAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,MAClD,oBAAoB,QAAQ;AAAA,UAC5B,sCAAqB,IAAI,QAAQ,EAAE;AAAA,IACrC,CAAC;AAED,UAAM,OAAO;AAAA,MACX,gBACI,uCAAoB,cAAc,QAAQ,EAAE,SAAS,IACrD;AAAA,MACJA;AAAA,IACF;AAEA,UAAM,WAAW,YAAAD,QAAK,KAAK,iBAAiB,QAAQ;AAEpD,QAAI,SAAS,UAAU;AACrB,UAAI,SAAS,WAAW;AACtB,eAAO,EAAE,aAAa,MAAM;AAAA,MAC9B;AAEA,gBAAM,2BAAU,UAAU,IAAI;AAC9B,aAAO;AAAA,QACL,aAAa;AAAA,QACb,KAAK,aAAa,OAAO,KAAK,QAAQ,CAAC;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,SAAS,WAAW;AACtB,aAAO;AAAA,QACL,aAAa;AAAA,QACb,KAAK,OAAO,OAAO;AAAA,UACjB;AAAA,QACF,CAAC,+BAA+B,OAAO;AAAA,UACrCC,gBAAe;AAAA,UACf;AAAA,UACA;AAAA,QACF,CAAC;AAAA,QACD;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,aAAa,MAAM;AAAA,EAC9B;AAEA,QAAM,iBAAiB,UAAM,4CAAqB,eAAe;AAEjE,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,yBAAyB;AAAA,MAAI,CAAC,SAC5B,kBAAkB,MAAM,cAAc;AAAA,IACxC;AAAA,EACF;AAGA,UAAQ,QAAQ,CAAC,WAAW;AAC1B,QAAI,OAAO,KAAK;AACd,aAAO,KAAK,OAAO,KAAK,OAAO,IAAI,sBAAsB,CAAC;AAAA,IAC5D;AAAA,EACF,CAAC;AAED,QAAM,kBAAkB,QAAQ,KAAK,CAAC,EAAE,YAAY,MAAM,WAAW;AAErE,SAAO;AAAA,IACL,IAAI,CAAC;AAAA,IACL,SAAS;AAAA,IACT,aAAa,QAAQ;AAAA,MAAQ,CAAC,EAAE,aAAa,UAAU,IAAI,MACzD,eAAe,MACX;AAAA,QACE;AAAA,UACE,MAAM;AAAA,UACN,aAAS,kBAAAC,SAAU,GAAG;AAAA,QACxB;AAAA,MACF,IACA,CAAC;AAAA,IACP;AAAA,EACF;AACF;AAEO,MAAM,wBAAwB,OACnC,MACA,WACgC;AAChC,MAAI;AACF,WAAO,MAAM,mBAAmB,MAAM,MAAM;AAAA,EAC9C,SAAS,KAAK;AACZ,WAAO,KAAK,iCAAiC;AAC7C,WAAO,WAAO,qBAAQ,GAAG,CAAC;AAE1B,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,SAAS;AAAA,MACT,aAAa,CAAC;AAAA,IAChB;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import path from 'path';\nimport { inspect } from 'util';\n\nimport { writeFile } from 'fs-extra';\nimport stripAnsi from 'strip-ansi';\n\nimport { Git } from '../../..';\nimport type { Logger } from '../../../utils/logging';\nimport { NPMRC_LINES, hasNpmrcSecret } from '../../../utils/npmrc';\nimport {\n type PackageManagerConfig,\n detectPackageManager,\n} from '../../../utils/packageManager';\nimport { readBaseTemplateFile } from '../../../utils/template';\nimport { getDestinationManifest } from '../../configure/analysis/package';\nimport { createDestinationFileReader } from '../../configure/analysis/project';\nimport { mergeWithConfigFile } from '../../configure/processing/configFile';\nimport type { InternalLintResult } from '../internal';\n\nconst ensureNoAuthToken = (fileContents: string) =>\n fileContents\n .split('\\n')\n .filter((line) => !hasNpmrcSecret(line))\n .join('\\n');\n\ntype RefreshableConfigFile = {\n name: string;\n type: 'ignore' | 'npmrc';\n additionalMapping?: (\n s: string,\n packageManager: PackageManagerConfig,\n ) => string;\n if?: (packageManager: PackageManagerConfig) => boolean;\n};\n\nconst removeRedundantNpmrc = (contents: string) => {\n const npmrcLines = contents\n .split('\\n')\n .filter((line) => NPMRC_LINES.includes(line.trim()));\n\n // If we're only left with !.npmrc line we can remove it\n // TODO: Consider if we should generalise this\n if (npmrcLines.length > 0 && npmrcLines.every((line) => line.includes('!'))) {\n return contents\n .split('\\n')\n .filter((line) => !NPMRC_LINES.includes(line.trim()))\n .join('\\n');\n }\n return contents;\n};\n\nexport const REFRESHABLE_CONFIG_FILES: RefreshableConfigFile[] = [\n { name: '.eslintignore', type: 'ignore' },\n {\n name: '.gitignore',\n type: 'ignore',\n additionalMapping: removeRedundantNpmrc,\n },\n { name: '.prettierignore', type: 'ignore' },\n {\n name: '.npmrc',\n type: 'npmrc',\n additionalMapping: ensureNoAuthToken,\n if: (packageManager: PackageManagerConfig) =>\n packageManager.command === 'pnpm',\n },\n {\n name: '.dockerignore',\n type: 'ignore',\n additionalMapping: removeRedundantNpmrc,\n },\n];\n\nexport const refreshConfigFiles = async (\n mode: 'format' | 'lint',\n logger: Logger,\n) => {\n const [manifest, gitRoot] = await Promise.all([\n getDestinationManifest(),\n Git.findRoot({ dir: process.cwd() }),\n ]);\n\n const destinationRoot = path.dirname(manifest.path);\n\n const readDestinationFile = createDestinationFileReader(destinationRoot);\n\n const refreshConfigFile = async (\n {\n name: filename,\n type: fileType,\n additionalMapping = (s) => s,\n if: condition = () => true,\n }: RefreshableConfigFile,\n packageManager: PackageManagerConfig,\n ) => {\n if (!condition(packageManager)) {\n return { needsChange: false };\n }\n\n const [inputFile, templateFile, isGitIgnored] = await Promise.all([\n readDestinationFile(filename),\n readBaseTemplateFile(`_${filename}`),\n gitRoot\n ? Git.isFileGitIgnored({\n gitRoot,\n absolutePath: path.join(destinationRoot, filename),\n })\n : false,\n ]);\n\n // If the file is gitignored and doesn't exist, don't make it\n if (inputFile === undefined && isGitIgnored) {\n return { needsChange: false };\n }\n\n const data = additionalMapping(\n inputFile\n ? mergeWithConfigFile(templateFile, fileType)(inputFile)\n : templateFile,\n packageManager,\n );\n\n const filepath = path.join(destinationRoot, filename);\n\n if (mode === 'format') {\n if (data === inputFile) {\n return { needsChange: false };\n }\n\n await writeFile(filepath, data);\n return {\n needsChange: false,\n msg: `Refreshed ${logger.bold(filename)}.`,\n filename,\n };\n }\n\n if (data !== inputFile) {\n return {\n needsChange: true,\n msg: `The ${logger.bold(\n filename,\n )} file is out of date. Run \\`${logger.bold(\n packageManager.exec,\n 'skuba',\n 'format',\n )}\\` to update it.`,\n filename,\n };\n }\n\n return { needsChange: false };\n };\n\n const packageManager = await detectPackageManager(destinationRoot);\n\n const results = await Promise.all(\n REFRESHABLE_CONFIG_FILES.map((conf) =>\n refreshConfigFile(conf, packageManager),\n ),\n );\n\n // Log after for reproducible test output ordering\n results.forEach((result) => {\n if (result.msg) {\n logger.warn(result.msg, logger.dim('refresh-config-files'));\n }\n });\n\n const anyNeedChanging = results.some(({ needsChange }) => needsChange);\n\n return {\n ok: !anyNeedChanging,\n fixable: anyNeedChanging,\n annotations: results.flatMap(({ needsChange, filename, msg }) =>\n needsChange && msg\n ? [\n {\n path: filename,\n message: stripAnsi(msg),\n },\n ]\n : [],\n ),\n };\n};\n\nexport const tryRefreshConfigFiles = async (\n mode: 'format' | 'lint',\n logger: Logger,\n): Promise<InternalLintResult> => {\n try {\n return await refreshConfigFiles(mode, logger);\n } catch (err) {\n logger.warn('Failed to refresh config files.');\n logger.subtle(inspect(err));\n\n return {\n ok: false,\n fixable: false,\n annotations: [],\n };\n }\n};\n"],
5
+ "mappings": "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",
6
6
  "names": ["path", "packageManager", "stripAnsi"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skuba",
3
- "version": "8.1.0-pm-20240430084912",
3
+ "version": "8.1.0-skuba-dive-secret-20240522031929",
4
4
  "private": false,
5
5
  "description": "SEEK development toolkit for backend applications and packages",
6
6
  "homepage": "https://github.com/seek-oss/skuba#readme",
@@ -132,7 +132,7 @@
132
132
  "type-fest": "2.19.0"
133
133
  },
134
134
  "peerDependencies": {
135
- "skuba-dive": "1 || 2"
135
+ "skuba-dive": "2.0.1-skuba-dive-secret-20240522031929"
136
136
  },
137
137
  "peerDependenciesMeta": {
138
138
  "skuba-dive": {
@@ -161,11 +161,11 @@
161
161
  "lint:md": "remark --frail --quiet .",
162
162
  "release": "pnpm --silent build && changeset publish",
163
163
  "skuba": "pnpm --silent build && pnpm --silent skuba:exec",
164
- "skuba:exec": "node --env-file=.env lib/skuba",
164
+ "skuba:exec": "node --experimental-vm-modules lib/skuba",
165
165
  "stage": "changeset version && node ./.changeset/inject.js && pnpm format",
166
- "test": "pnpm --silent skuba test --selectProjects unit",
166
+ "test": "pnpm --silent skuba test --selectProjects unit --",
167
167
  "test:ci": "pnpm --silent skuba test --runInBand",
168
- "test:int": "pnpm --silent skuba test --runInBand --selectProjects integration",
168
+ "test:int": "pnpm --silent skuba test --selectProjects integration --runInBand",
169
169
  "test:template": "scripts/test-template.sh",
170
170
  "test:watch": "pnpm --silent skuba test --runInBand --watch"
171
171
  }
@@ -0,0 +1,3 @@
1
+ {
2
+ "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
3
+ }
@@ -1,6 +1,7 @@
1
1
  # managed by skuba
2
2
  .idea/*
3
3
  .vscode/*
4
+ !.vscode/extensions.json
4
5
 
5
6
  .cdk.staging/
6
7
  .serverless/
@@ -14,6 +14,8 @@
14
14
  "dependencies": {
15
15
  "@seek/logger": "^6.0.0",
16
16
  "express": "^4.17.1",
17
+ "hot-shots": "^10.0.0",
18
+ "seek-datadog-custom-metrics": "^4.6.3",
17
19
  "skuba-dive": "^2.0.0"
18
20
  },
19
21
  "devDependencies": {
@@ -7,8 +7,8 @@ interface Config {
7
7
  name: string;
8
8
  version: string;
9
9
 
10
- metricsServer?: string;
11
- port?: number;
10
+ metricsServer: string | null;
11
+ port: number | null;
12
12
  }
13
13
 
14
14
  type Environment = (typeof environments)[number];
@@ -26,19 +26,27 @@ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
26
26
  logLevel: 'debug',
27
27
  name: '<%- serviceName %>',
28
28
  version: 'local',
29
+
30
+ metricsServer: null,
31
+ port: null,
29
32
  }),
30
33
 
31
34
  test: () => ({
32
- ...configs.local(),
33
-
34
35
  logLevel: Env.string('LOG_LEVEL', { default: 'silent' }),
36
+ name: '<%- serviceName %>',
35
37
  version: 'test',
38
+
39
+ metricsServer: null,
40
+ port: null,
36
41
  }),
37
42
 
38
43
  [dev]: () => ({
39
- ...configs[prod](),
40
-
41
44
  logLevel: 'debug',
45
+ name: Env.string('SERVICE'),
46
+ version: Env.string('VERSION'),
47
+
48
+ metricsServer: 'localhost',
49
+ port: Env.nonNegativeInteger('PORT'),
42
50
  }),
43
51
 
44
52
  [prod]: () => ({
@@ -2,7 +2,7 @@ import createLogger from '@seek/logger';
2
2
 
3
3
  import { config } from 'src/config';
4
4
 
5
- export const rootLogger = createLogger({
5
+ export const logger = createLogger({
6
6
  base: {
7
7
  environment: config.environment,
8
8
  version: config.version,
@@ -0,0 +1,11 @@
1
+ import { StatsD } from 'hot-shots';
2
+ import { createStatsDClient } from 'seek-datadog-custom-metrics';
3
+
4
+ import { config } from 'src/config';
5
+
6
+ import { logger } from './logging';
7
+
8
+ /* istanbul ignore next: StatsD client is not our responsibility */
9
+ export const metricsClient = createStatsDClient(StatsD, config, (err) =>
10
+ logger.error({ err }, 'StatsD error'),
11
+ );
@@ -2,7 +2,7 @@ import './register';
2
2
 
3
3
  import app from './app';
4
4
  import { config } from './config';
5
- import { rootLogger } from './framework/logging';
5
+ import { logger } from './framework/logging';
6
6
 
7
7
  // If your application is deployed with more than 1 vCPU you can delete this
8
8
  // file and use a clustering utility to run `lib/app`.
@@ -11,7 +11,7 @@ const listener = app.listen(config.port, () => {
11
11
  const address = listener.address();
12
12
 
13
13
  if (typeof address === 'object' && address) {
14
- rootLogger.debug(`listening on port ${address.port}`);
14
+ logger.debug(`listening on port ${address.port}`);
15
15
  }
16
16
  });
17
17
 
@@ -26,7 +26,7 @@
26
26
  "koa": "^2.13.4",
27
27
  "koa-bodyparser": "^4.3.0",
28
28
  "koa-compose": "^4.2.0",
29
- "seek-datadog-custom-metrics": "^4.2.1",
29
+ "seek-datadog-custom-metrics": "^4.6.3",
30
30
  "seek-koala": "^7.0.0",
31
31
  "skuba-dive": "^2.0.0",
32
32
  "zod": "^3.19.1"
@@ -7,8 +7,8 @@ interface Config {
7
7
  name: string;
8
8
  version: string;
9
9
 
10
- metricsServer?: string;
11
- port?: number;
10
+ metricsServer: string | null;
11
+ port: number | null;
12
12
  }
13
13
 
14
14
  type Environment = (typeof environments)[number];
@@ -26,19 +26,29 @@ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
26
26
  logLevel: 'debug',
27
27
  name: '<%- serviceName %>',
28
28
  version: 'local',
29
+
30
+ metricsServer: null,
31
+ port: null,
29
32
  }),
30
33
 
31
34
  test: () => ({
32
- ...configs.local(),
33
-
34
35
  logLevel: Env.string('LOG_LEVEL', { default: 'silent' }),
36
+ name: '<%- serviceName %>',
35
37
  version: 'test',
38
+
39
+ metricsServer: null,
40
+ port: null,
36
41
  }),
37
42
 
38
43
  [dev]: () => ({
39
44
  ...configs[prod](),
40
45
 
41
46
  logLevel: 'debug',
47
+ name: Env.string('SERVICE'),
48
+ version: Env.string('VERSION'),
49
+
50
+ metricsServer: 'localhost',
51
+ port: Env.nonNegativeInteger('PORT'),
42
52
  }),
43
53
 
44
54
  [prod]: () => ({
@@ -29,16 +29,21 @@ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
29
29
  }),
30
30
 
31
31
  test: () => ({
32
- ...configs.local(),
33
-
34
32
  logLevel: Env.string('LOG_LEVEL', { default: 'silent' }),
33
+ metrics: false,
34
+ name: '<%- serviceName %>',
35
35
  version: 'test',
36
+
37
+ destinationSnsTopicArn: 'arn:aws:sns:us-east-2:123456789012:destination',
36
38
  }),
37
39
 
38
40
  dev: () => ({
39
- ...configs.prod(),
40
-
41
41
  logLevel: 'debug',
42
+ metrics: true,
43
+ name: Env.string('SERVICE'),
44
+ version: Env.string('VERSION'),
45
+
46
+ destinationSnsTopicArn: Env.string('DESTINATION_SNS_TOPIC_ARN'),
42
47
  }),
43
48
 
44
49
  prod: () => ({
@@ -451,6 +451,8 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
451
451
  "ENVIRONMENT": "dev",
452
452
  "NODE_ENV": "production",
453
453
  "NODE_OPTIONS": "--enable-source-maps",
454
+ "SERVICE": "serviceName",
455
+ "VERSION": "local",
454
456
  },
455
457
  },
456
458
  "FunctionName": "serviceName",
@@ -644,6 +646,8 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
644
646
  },
645
647
  "NODE_ENV": "production",
646
648
  "NODE_OPTIONS": "--enable-source-maps",
649
+ "SERVICE": "serviceName",
650
+ "VERSION": "local",
647
651
  },
648
652
  },
649
653
  "FunctionName": "serviceName-post-hook",
@@ -796,6 +800,8 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
796
800
  },
797
801
  "NODE_ENV": "production",
798
802
  "NODE_OPTIONS": "--enable-source-maps",
803
+ "SERVICE": "serviceName",
804
+ "VERSION": "local",
799
805
  },
800
806
  },
801
807
  "FunctionName": "serviceName-pre-hook",
@@ -1493,6 +1499,8 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
1493
1499
  "ENVIRONMENT": "prod",
1494
1500
  "NODE_ENV": "production",
1495
1501
  "NODE_OPTIONS": "--enable-source-maps",
1502
+ "SERVICE": "serviceName",
1503
+ "VERSION": "local",
1496
1504
  },
1497
1505
  },
1498
1506
  "FunctionName": "serviceName",
@@ -1686,6 +1694,8 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
1686
1694
  },
1687
1695
  "NODE_ENV": "production",
1688
1696
  "NODE_OPTIONS": "--enable-source-maps",
1697
+ "SERVICE": "serviceName",
1698
+ "VERSION": "local",
1689
1699
  },
1690
1700
  },
1691
1701
  "FunctionName": "serviceName-post-hook",
@@ -1838,6 +1848,8 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
1838
1848
  },
1839
1849
  "NODE_ENV": "production",
1840
1850
  "NODE_OPTIONS": "--enable-source-maps",
1851
+ "SERVICE": "serviceName",
1852
+ "VERSION": "local",
1841
1853
  },
1842
1854
  },
1843
1855
  "FunctionName": "serviceName-pre-hook",
@@ -1,27 +1,33 @@
1
- import { z } from 'zod';
1
+ import { Env } from 'skuba-dive';
2
2
 
3
- const environment = z.enum(['dev', 'prod']).parse(process.env.ENVIRONMENT);
3
+ const ENVIRONMENTS = ['dev', 'prod'] as const;
4
4
 
5
- type Environment = typeof environment;
5
+ type Environment = (typeof ENVIRONMENTS)[number];
6
6
 
7
- export interface Config {
7
+ const environment = Env.oneOf(ENVIRONMENTS)('ENVIRONMENT');
8
+
9
+ interface Config {
8
10
  appName: string;
9
11
  workerLambda: {
10
12
  reservedConcurrency: number;
11
13
  environment: {
12
14
  ENVIRONMENT: Environment;
15
+ SERVICE: string;
16
+ VERSION: string;
13
17
  };
14
18
  };
15
19
  sourceSnsTopicArn: string;
16
20
  }
17
21
 
18
- export const configs: Record<Environment, Config> = {
22
+ const configs: Record<Environment, Config> = {
19
23
  dev: {
20
24
  appName: '<%- serviceName %>',
21
25
  workerLambda: {
22
26
  reservedConcurrency: 2,
23
27
  environment: {
24
28
  ENVIRONMENT: 'dev',
29
+ SERVICE: '<%- serviceName %>',
30
+ VERSION: Env.string('VERSION', { default: 'local' }),
25
31
  },
26
32
  },
27
33
  sourceSnsTopicArn: 'TODO: sourceSnsTopicArn',
@@ -32,10 +38,12 @@ export const configs: Record<Environment, Config> = {
32
38
  reservedConcurrency: 20,
33
39
  environment: {
34
40
  ENVIRONMENT: 'prod',
41
+ SERVICE: '<%- serviceName %>',
42
+ VERSION: Env.string('VERSION', { default: 'local' }),
35
43
  },
36
44
  },
37
45
  sourceSnsTopicArn: 'TODO: sourceSnsTopicArn',
38
46
  },
39
47
  };
40
48
 
41
- export const config = configs[environment];
49
+ export const config: Config = configs[environment];
@@ -16,6 +16,7 @@
16
16
  "@aws-sdk/client-lambda": "^3.363.0",
17
17
  "@aws-sdk/client-sns": "^3.363.0",
18
18
  "@seek/logger": "^6.0.0",
19
+ "skuba-dive": "^2.0.0",
19
20
  "zod": "^3.19.1"
20
21
  },
21
22
  "devDependencies": {
@@ -24,6 +25,7 @@
24
25
  "aws-cdk": "^2.109.0",
25
26
  "aws-cdk-lib": "^2.109.0",
26
27
  "constructs": "^10.0.17",
28
+ "pino-pretty": "^11.0.0",
27
29
  "skuba": "*"
28
30
  },
29
31
  "packageManager": "pnpm@9.0.2",
@@ -1,8 +1,20 @@
1
1
  import createLogger from '@seek/logger';
2
2
  import type { SQSEvent, SQSHandler } from 'aws-lambda';
3
3
 
4
- const logger = createLogger({
5
- name: '<%- serviceName %>',
4
+ import { config } from './config';
5
+
6
+ export const logger = createLogger({
7
+ base: {
8
+ environment: config.environment,
9
+ version: config.version,
10
+ },
11
+
12
+ level: config.logLevel,
13
+
14
+ name: config.name,
15
+
16
+ transport:
17
+ config.environment === 'local' ? { target: 'pino-pretty' } : undefined,
6
18
  });
7
19
 
8
20
  /**
@@ -0,0 +1,47 @@
1
+ import { Env } from 'skuba-dive';
2
+
3
+ interface Config {
4
+ environment: Environment;
5
+
6
+ logLevel: string;
7
+ name: string;
8
+ version: string;
9
+ }
10
+
11
+ type Environment = (typeof environments)[number];
12
+
13
+ const environments = ['local', 'test', 'dev', 'prod'] as const;
14
+
15
+ const environment = Env.oneOf(environments)('ENVIRONMENT');
16
+
17
+ /* istanbul ignore next: config verification makes more sense in a smoke test */
18
+ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
19
+ local: () => ({
20
+ logLevel: 'debug',
21
+ name: '<%- serviceName %>',
22
+ version: 'local',
23
+ }),
24
+
25
+ test: () => ({
26
+ logLevel: Env.string('LOG_LEVEL', { default: 'silent' }),
27
+ name: '<%- serviceName %>',
28
+ version: 'test',
29
+ }),
30
+
31
+ dev: () => ({
32
+ logLevel: 'debug',
33
+ name: Env.string('SERVICE'),
34
+ version: Env.string('VERSION'),
35
+ }),
36
+
37
+ prod: () => ({
38
+ logLevel: 'info',
39
+ name: Env.string('SERVICE'),
40
+ version: Env.string('VERSION'),
41
+ }),
42
+ };
43
+
44
+ export const config: Config = {
45
+ ...configs[environment](),
46
+ environment,
47
+ };