forked from actions/starter-workflows
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
executable file
·149 lines (134 loc) · 5.12 KB
/
Copy pathindex.ts
File metadata and controls
executable file
·149 lines (134 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env npx ts-node
import { promises as fs } from "fs";
import { safeLoad } from "js-yaml";
import { basename, extname, join, dirname } from "path";
import { Validator as validator } from "jsonschema";
import { endGroup, error, info, setFailed, startGroup } from '@actions/core';
interface WorkflowWithErrors {
id: string;
name: string;
errors: string[];
}
interface WorkflowProperties {
name: string;
description: string;
creator: string;
iconName: string;
categories: string[];
}
const propertiesSchema = {
type: "object",
properties: {
name: { type: "string", required: true , "minLength": 1},
description: { type: "string", required: true },
creator: { type: "string", required: false },
iconName: { type: "string", required: true },
categories: {
anyOf: [
{
type: "array",
items: { type: "string" }
},
{
type: "null",
}
],
required: true
},
}
}
async function checkWorkflows(folders: string[], allowed_categories: object[]): Promise<WorkflowWithErrors[]> {
const result: WorkflowWithErrors[] = []
const workflow_template_names = new Set()
for (const folder of folders) {
const dir = await fs.readdir(folder, {
withFileTypes: true,
});
for (const e of dir) {
if (e.isFile() && [".yml", ".yaml"].includes(extname(e.name))) {
const fileType = basename(e.name, extname(e.name))
const workflowFilePath = join(folder, e.name);
const propertiesFilePath = join(folder, "properties", `${fileType}.properties.json`)
const workflowWithErrors = await checkWorkflow(workflowFilePath, propertiesFilePath, allowed_categories);
if(workflowWithErrors.name && workflow_template_names.size == workflow_template_names.add(workflowWithErrors.name).size) {
workflowWithErrors.errors.push(`Workflow template name "${workflowWithErrors.name}" already exists`)
}
if (workflowWithErrors.errors.length > 0) {
result.push(workflowWithErrors)
}
}
}
}
return result;
}
async function checkWorkflow(workflowPath: string, propertiesPath: string, allowed_categories: object[]): Promise<WorkflowWithErrors> {
let workflowErrors: WorkflowWithErrors = {
id: workflowPath,
name: null,
errors: []
}
try {
const workflowFileContent = await fs.readFile(workflowPath, "utf8");
safeLoad(workflowFileContent); // Validate yaml parses without error
const propertiesFileContent = await fs.readFile(propertiesPath, "utf8")
const properties: WorkflowProperties = JSON.parse(propertiesFileContent)
if(properties.name && properties.name.trim().length > 0) {
workflowErrors.name = properties.name
}
let v = new validator();
const res = v.validate(properties, propertiesSchema)
workflowErrors.errors = res.errors.map(e => e.toString())
if (properties.iconName) {
if(! /^octicon\s+/.test(properties.iconName)) {
try {
await fs.access(`../../icons/${properties.iconName}.svg`)
} catch (e) {
workflowErrors.errors.push(`No icon named ${properties.iconName} found`)
}
}
else {
let iconName = properties.iconName.match(/^octicon\s+(.*)/)
if(!iconName || iconName[1].split(".")[0].length <= 0) {
workflowErrors.errors.push(`No icon named ${properties.iconName} found`)
}
}
}
var path = dirname(workflowPath)
var folder_categories = allowed_categories.find( category => category["path"] == path)["categories"]
if (!workflowPath.endsWith("blank.yml")) {
if(!properties.categories || properties.categories.length == 0) {
workflowErrors.errors.push(`Workflow categories cannot be null or empty`)
}
else if(!folder_categories.some(category => properties.categories[0].toLowerCase() == category.toLowerCase())) {
workflowErrors.errors.push(`The first category in properties.json categories for workflow in ${basename(path)} folder must be one of "${folder_categories}. Either move the workflow to an appropriate directory or change the category."`)
}
}
if(basename(path).toLowerCase() == 'deployments' && !properties.creator) {
workflowErrors.errors.push(`The "creator" in properties.json must be present.`)
}
} catch (e) {
workflowErrors.errors.push(e.toString())
}
return workflowErrors;
}
(async function main() {
try {
const settings = require("./settings.json");
const erroredWorkflows = await checkWorkflows(
settings.folders, settings.allowed_categories
)
if (erroredWorkflows.length > 0) {
startGroup(`😟 - Found ${erroredWorkflows.length} workflows with errors:`);
erroredWorkflows.forEach(erroredWorkflow => {
error(`Errors in ${erroredWorkflow.id} - ${erroredWorkflow.errors.map(e => e.toString()).join(", ")}`)
})
endGroup();
setFailed(`Found ${erroredWorkflows.length} workflows with errors`);
} else {
info("🎉🤘 - Found no workflows with errors!")
}
} catch (e) {
error(`Unhandled error while syncing workflows: ${e}`);
setFailed(`Unhandled error`)
}
})();