Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing spec #90

Merged
merged 2 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
"Matthew White <whitemat@uk.ibm.com>",
"Bence Dányi <bence.danyi@ibm.com>"
],
"files":[
"/dist",".tektonlintrc.yaml"
]
,
"license": "Apache-2.0",
"dependencies": {
"chalk": "^4.1.2",
Expand Down
17 changes: 17 additions & 0 deletions src/interfaces/common.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Document } from 'yaml';

interface Base {
apiVersion: string;
metadata: {
Expand Down Expand Up @@ -42,4 +44,19 @@ interface Tekton {
externaltasks?: ExternalResource[];
}

// ref: https://dev.to/ankittanna/how-to-create-a-type-for-complex-json-object-in-typescript-d81
type JSONValue = string | number | boolean | { [x: string]: JSONValue } | Array<JSONValue>;

export interface Doc {
content: JSONValue;
doc: Document;
path: string;
raw: string;
no_report: boolean;
}

export type RuleReportFn = (message: string, node, prop) => void;

export type RuleFn = (docs, tekton: Tekton, report: RuleReportFn) => void;

export { Tekton, Base, Param, BaseName, ValueParam, ExternalResource };
9 changes: 7 additions & 2 deletions src/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { toolConfig } from './config.js';
import run from './runner.js';
import logProblems from './log-problems.js';

import { logger } from './logger.js';
import { logger, usingLogfile } from './logger.js';

const p = path.resolve(path.dirname(new url.URL(import.meta.url).pathname), '..', 'package.json');
const pkg = JSON.parse(fs.readFileSync(p, 'utf-8'));
Expand Down Expand Up @@ -67,7 +67,12 @@ const parser = yargs(process.argv.slice(2))
process.exitCode = 0;
}
} catch (e) {
logger.error((e as Error).message);
if (usingLogfile()) {
logger.error(e as Error);
} else {
logger.error((e as Error).message);
}

process.exitCode = 1;
}
})();
11 changes: 10 additions & 1 deletion src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,13 @@ if (logfile) {
});
}

export const logger = pino.pino(transports);
export const logger = pino.pino(
{
serializers: {
err: pino.stdSerializers.err,
},
},
transports,
);

export const usingLogfile = () => logfile;
14 changes: 8 additions & 6 deletions src/reporter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
function getLine(chars, n) {
import { Doc } from './interfaces/common.js';

function getLine(chars: string[], n: number): number {
let l = 1;
for (let i = 0; i < n; i++) {
if (chars[i] === '\n') l++;
}
return l;
}

function getCol(chars, n) {
function getCol(chars: string[], n: number): number {
let c = 1;
for (let i = 0; i < n; i++) {
if (chars[i] === '\n') c = 0;
Expand All @@ -19,7 +21,7 @@ function getLocation(m, node, prop) {
if (!m.has(node)) return {};
const k = m.get(node);

const chars = Array.from(k.doc.raw);
const chars: string[] = Array.from(k.doc.raw);
let n = prop ? k.node.get(prop, true) : k.node;
if (!n) n = k.node.items.find((pair) => pair.key.value === prop).key;
return {
Expand Down Expand Up @@ -51,11 +53,11 @@ function walk(node, path, visitor) {
}
}

function instrument(docs) {
function instrument(docs: Doc[]) {
const m = new Map();
for (const doc of docs) {
walk(doc.content, [], (node, path) => {
if (node != null && typeof node == 'object') {
if (node != null && typeof node == 'object') {
m.set(node, {
node: path.length ? doc.doc.getIn(path, true) : doc.doc,
path,
Expand All @@ -71,7 +73,7 @@ class Reporter {
private m: any;
problems: any[];

constructor(docs = []) {
constructor(docs: Doc[] = []) {
this.m = instrument(docs);
this.problems = [];
}
Expand Down
2 changes: 2 additions & 0 deletions src/rules/no-duplicate-env.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export default (docs, tekton, report) => {
for (const task of Object.values<any>(tekton.tasks)) {
if (!task.spec) continue;

if (task.spec.stepTemplate && task.spec.stepTemplate.env) {
const templateEnvVars = new Set();
for (const env of task.spec.stepTemplate.env) {
Expand Down
2 changes: 2 additions & 0 deletions src/rules/no-invalid-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ function getTaskParams(spec) {

function checkInvalidParameterName(resources, report) {
for (const resource of Object.values<any>(resources)) {
if (!resource.spec) continue;

let params;
if (resource.kind === 'Task') {
params = getTaskParams(resource.spec);
Expand Down
7 changes: 4 additions & 3 deletions src/rules/no-invalid-param-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,24 @@ export default (docs, tekton, report) => {
}

for (const task of Object.values<any>(tekton.tasks)) {
if (!task.spec) continue;
const params = getTaskParams(task.spec);
if (!params) continue;
checkParameterValues(task.metadata.name, task.kind, params, report);
}

for (const template of Object.values<any>(tekton.triggerTemplates)) {
if (!template.spec.params) continue;
if (!template.spec || !template.spec.params) continue;
checkParameterValues(template.metadata.name, template.kind, template.spec.params, report);
}

for (const pipeline of Object.values<any>(tekton.pipelines)) {
if (!pipeline.spec.params) continue;
if (!pipeline.spec || !pipeline.spec.params) continue;
checkParameterValues(pipeline.metadata.name, pipeline.kind, pipeline.spec.params, report);
}

for (const pipeline of Object.values<any>(tekton.pipelines)) {
if (!pipeline.spec.params) continue;
if (!pipeline.spec || !pipeline.spec.params) continue;
for (const task of Object.values<any>(pipeline.spec.tasks)) {
if (!task.params) continue;
for (const param of Object.values<any>(task.params)) {
Expand Down
1 change: 1 addition & 0 deletions src/rules/no-latest-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default (docs, tekton, report) => {
}

for (const task of Object.values<any>(tekton.tasks)) {
if (!task.spec) continue;
check(task.spec.stepTemplate);
for (const step of Object.values(task.spec.steps)) {
check(step);
Expand Down
1 change: 1 addition & 0 deletions src/rules/no-missing-hashbang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const checkSteps = (steps, report) => {

export default (docs, tekton, report) => {
for (const task of Object.values<any>(tekton.tasks)) {
if (!task.spec) continue;
checkSteps(task.spec.steps, report);
}

Expand Down
4 changes: 2 additions & 2 deletions src/rules/no-missing-workspace.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default (docs, tekton, report) => {
for (const task of Object.values<any>(tekton.tasks)) {
if (!task.spec.workspaces) continue;
if (!task.spec || !task.spec.workspaces) continue;
const taskName = task.metadata.name;
const requiredWorkspaces = task.spec.workspaces.filter((ws) => !ws.optional).map((ws) => ws.name);

Expand Down Expand Up @@ -42,7 +42,7 @@ export default (docs, tekton, report) => {
}

for (const pipeline of Object.values<any>(tekton.pipelines)) {
if (!pipeline.spec.workspaces) continue;
if (!pipeline.spec || !pipeline.spec.workspaces) continue;
const required = pipeline.spec.workspaces.map((ws) => ws.name);

for (const template of Object.values<any>(tekton.triggerTemplates)) {
Expand Down
1 change: 1 addition & 0 deletions src/rules/no-undefined-param.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default (docs, tekton, report) => {
}

for (const task of Object.values<any>(tekton.tasks)) {
if (!task.spec) continue;
const params = getTaskParams(task);
for (const prefix of ['inputs.params', 'params']) {
for (const prop of ['steps', 'volumes', 'stepTemplate', 'sidecars']) {
Expand Down
1 change: 1 addition & 0 deletions src/rules/no-undefined-volume.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export default (docs, tekton, report) => {
for (const task of Object.values<any>(tekton.tasks)) {
if (!task.spec) continue;
const volumes = (task.spec.volumes || []).map((volume) => volume.name);

for (const step of task.spec.steps) {
Expand Down
4 changes: 4 additions & 0 deletions src/rules/no-unused-param.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ function getParams(kind, spec) {

export default (docs, tekton, report) => {
for (const task of Object.values<any>(tekton.tasks)) {
if (!task.spec) continue;
const params = getParams(task.kind, task.spec);
const occurences = Object.fromEntries(params.map((param) => [param.name, 0]));
for (const prefix of ['inputs.params', 'params']) {
Expand All @@ -41,6 +42,7 @@ export default (docs, tekton, report) => {
}

for (const condition of Object.values<any>(tekton.conditions)) {
if (!condition.spec) continue;
const params = getParams(condition.kind, condition.spec);
const occurences = Object.fromEntries(params.map((param) => [param.name, 0]));
walk(condition.spec.check, ['spec', 'check'], unused(occurences, 'params'));
Expand All @@ -54,6 +56,7 @@ export default (docs, tekton, report) => {
}

for (const template of Object.values<any>(tekton.triggerTemplates)) {
if (!template.spec) continue;
const params = getParams(template.kind, template.spec);
const occurences = Object.fromEntries(params.map((param) => [param.name, 0]));
walk(template.spec, ['spec'], unused(occurences, 'params'));
Expand All @@ -67,6 +70,7 @@ export default (docs, tekton, report) => {
}

for (const pipeline of Object.values<any>(tekton.pipelines)) {
if (!pipeline.spec) continue;
const params = getParams(pipeline.kind, pipeline.spec);
const occurences = Object.fromEntries(params.map((param) => [param.name, 0]));
walk(pipeline.spec.tasks, ['spec', 'tasks'], unused(occurences, 'params'));
Expand Down
3 changes: 2 additions & 1 deletion src/rules/prefer-beta.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export default (docs, tekton, report) => {
for (const pipeline of Object.values<any>(tekton.pipelines)) {
if (!pipeline.spec) continue;
for (const task of pipeline.spec.tasks) {
if (!task.taskSpec) continue;
switch (pipeline.apiVersion) {
case 'tekton.dev/v1alpha1':
if (task.taskSpec.params)
Expand All @@ -22,6 +22,7 @@ export default (docs, tekton, report) => {
}

for (const task of Object.values<any>(tekton.tasks)) {
if (!task.spec) continue;
switch (task.apiVersion) {
case 'tekton.dev/v1alpha1':
if (task.spec.params)
Expand Down
3 changes: 2 additions & 1 deletion src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import yaml from 'yaml';
import fs from 'node:fs';
import { collectAllExternal } from './external.js';
import { logger } from './logger.js';
import { Doc } from './interfaces/common.js';

/* Collect paths based on the glob pattern passed in */
const collector = async (paths: string[], cfg: ToolConfig) => {
const docs: any = [];
const docs: Doc[] = [];
const files = await glob(paths);
logger.info('Found these files %j', files);
for (const file of files) {
Expand Down
Loading