Skip to content

Commit 8002eef

Browse files
committed
Add plugin dependency options and alias, use matrix for GitHub Actions
1 parent ec965b6 commit 8002eef

File tree

5 files changed

+174
-37
lines changed

5 files changed

+174
-37
lines changed

.github/workflows/main.yml

+7-21
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,12 @@ name: Test behaviour
33
on: [push]
44

55
jobs:
6-
minimal:
7-
runs-on: ubuntu-latest
8-
name: Minimal plugin.json
9-
steps:
10-
- name: Checkout
11-
uses: actions/checkout@v4
12-
13-
- name: Test minimal.json
14-
id: plugin
15-
uses: ./
16-
with:
17-
file: test/minimal.json
18-
19-
- name: Plugin info
20-
shell: bash
21-
run: |
22-
echo "Codename: ${{steps.plugin.outputs.codename}}"
23-
echo "Version: ${{steps.plugin.outputs.version}}"
24-
256
full:
267
runs-on: ubuntu-latest
27-
name: Full plugin.json
8+
strategy:
9+
matrix:
10+
plugin_file: [ 'minimal.json', 'full.json' ]
11+
name: ${{ matrix.plugin_file }}
2812
steps:
2913
- name: Checkout
3014
uses: actions/checkout@v4
@@ -33,10 +17,12 @@ jobs:
3317
id: plugin
3418
uses: ./
3519
with:
36-
file: test/full.json
20+
file: 'test/${{ matrix.plugin_file }}'
3721

3822
- name: Plugin info
3923
shell: bash
4024
run: |
4125
echo "Codename: ${{steps.plugin.outputs.codename}}"
4226
echo "Version: ${{steps.plugin.outputs.version}}"
27+
echo "Alias: ${{steps.plugin.outputs.alias}}"
28+
echo "Other Plugins: ${{steps.plugin.outputs.other_plugins}}"

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,28 @@ Causes the validator to not check against official names.
2424
Do not use this unless you are an official plugin.
2525
It does not give you any advantage, just ignores checks for official-sounding names.
2626

27+
## Outputs
28+
29+
### `codename`
30+
31+
Parsed codename from the plugin.
32+
33+
### `version`
34+
35+
Parsed version from the plugin.
36+
37+
### `alias`
38+
39+
Comma-separated list of codenames.
40+
41+
### `depends`
42+
43+
Plugins this one directly depends on.
44+
45+
### `other_plugins`
46+
47+
Other plugins mentioned in plugin relations like `depends` or `recommends`.
48+
2749
## Example usage
2850

2951
```yaml

action.yml

+6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ outputs:
1313
description: 'Plugin codename'
1414
version:
1515
description: 'Plugin version'
16+
alias:
17+
description: 'Alternative plugin codenames separated by a comma'
18+
depends:
19+
description: 'Comma-separated plugins this one directly depends on'
20+
other_plugins:
21+
description: 'Comma-separated plugins mentioned in plugin relation fields'
1622
runs:
1723
using: 'node20'
1824
main: 'index.js'

index.js

+119-15
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,26 @@ function checkApiVersion(api_version)
2828
break;
2929
}
3030
}
31-
function checkCodename(codename)
31+
function validateCodename(field, value)
3232
{
33-
if(typeof codename !== "string")
33+
if(typeof value !== "string")
3434
{
35-
core.error("`codename` must be a string");
35+
core.error(field + " must be a string");
3636
return;
3737
}
3838

39-
if(codename.length === 0)
39+
if(value.length === 0)
4040
{
41-
core.error("`codename` cannot be empty");
41+
core.error(field + " cannot be empty");
4242
return;
4343
}
44-
else if(codename.length < 2)
44+
else if(value.length < 2)
4545
{
46-
core.error("`codename` must be at least 2 characters");
46+
core.error(field + " must be at least 2 characters");
4747
return;
4848
}
49-
else if(codename.length < 8)
50-
core.warning("`codename` should be at least 8 characters");
49+
else if(value.length < 8)
50+
core.warning(field + " should be at least 8 characters");
5151

5252
{
5353
// - Only alphanumeric characters
@@ -56,19 +56,56 @@ function checkCodename(codename)
5656
// - No number at the beginning (but may be at the end)
5757
const regex_req = /^[a-zA-Z]([_a-zA-Z0-9]?[a-zA-Z0-9])*$/;
5858
const regex_min = /^[a-z]([_a-z0-9]?[a-z0-9])*$/;
59-
if(!regex_req.test(codename))
59+
if(!regex_req.test(value))
6060
{
61-
core.error("`codename` does not match required format - only alphanumeric characters with optional underscore");
61+
core.error(field + " does not match required format - only alphanumeric characters with optional underscore");
6262
return;
6363
}
64-
else if(!regex_min.test(codename))
65-
core.warning("`codename` does not match expected format - only lowercase letters, numbers and optional underscores");
64+
else if(!regex_min.test(value))
65+
core.warning(field + " does not match expected format - only lowercase letters, numbers and optional underscores");
6666
}
6767

68-
if(codename.startsWith('vl_') || codename.startsWith('VL_') || codename.startsWith('voxelite_') || codename.startsWith('VOXELITE_'))
68+
if(value.startsWith('vl_') || value.startsWith('VL_') || value.startsWith('voxelite_') || value.startsWith('VOXELITE_'))
6969
{
7070
if(!voxeliteOfficial)
71-
core.warning("It looks like you are using `codename` similar to official Voxelite ones, please choose a different prefix");
71+
core.warning("It looks like you are using " + field + " similar to official Voxelite ones, please choose a different prefix");
72+
}
73+
}
74+
function checkCodename(codename)
75+
{
76+
validateCodename("`codename`", codename);
77+
}
78+
function checkAlias(alias)
79+
{
80+
validateCodename(`alias '${alias}'`, alias);
81+
}
82+
function checkAliases(alias)
83+
{
84+
switch(typeof alias)
85+
{
86+
case "string":
87+
{
88+
checkAlias(alias);
89+
return [ alias ];
90+
}
91+
case "object":
92+
{
93+
if(Array.isArray(alias) && alias.every(item => typeof item === "string"))
94+
{
95+
alias.forEach(value => {
96+
checkAlias(value);
97+
});
98+
return alias;
99+
}
100+
else
101+
{
102+
core.error("`alias` should be a string or an array");
103+
return [];
104+
}
105+
}
106+
default:
107+
core.error("`alias` should be a string or an array");
108+
return [];
72109
}
73110
}
74111
function validateNonPrintCharacter(field, value)
@@ -288,6 +325,40 @@ function checkPermission(permission)
288325
}
289326
}
290327
}
328+
function checkPluginRelations(field, value, allowRelative)
329+
{
330+
if(typeof value == "object" && Object.keys(value).every(item => typeof item === "string") && Object.values(value).every(item => typeof item === "string"))
331+
{
332+
const versionRegex = /^([0-9]+(\.[0-9]+){0,2})?$/
333+
334+
let pluginNames = new Set();
335+
Object.keys(value).forEach(key => {
336+
let keyValue = value[key];
337+
338+
validateCodename(`${field} plugin`, key);
339+
pluginNames.add(key);
340+
341+
// Validate version in `value` with `allowRelative` option
342+
if(allowRelative)
343+
{
344+
if(keyValue.startsWith('<') || keyValue.startsWith('>') || keyValue.startsWith('='))
345+
keyValue = keyValue.substring(1);
346+
if(keyValue.startsWith('<=') || keyValue.startsWith('>='))
347+
keyValue = keyValue.substring(2);
348+
}
349+
if(!versionRegex.test(keyValue))
350+
core.error("Invalid version string: " + keyValue);
351+
352+
//TODO Check for colliding dependencies (same plugin but incompatible versions)
353+
});
354+
return pluginNames;
355+
}
356+
else
357+
{
358+
core.error(field + " plugin should be a string or an array");
359+
return new Set();
360+
}
361+
}
291362

292363
try
293364
{
@@ -322,6 +393,12 @@ try
322393
checkCodename(json['codename']);
323394
core.setOutput('codename', json['codename']);
324395
}
396+
// alias
397+
if(json.hasOwnProperty("alias"))
398+
{
399+
const alias = checkAliases(json['alias']);
400+
core.setOutput('alias', alias.join(','));
401+
}
325402
// version
326403
if(!json.hasOwnProperty("version"))
327404
core.error('Missing `version`');
@@ -365,6 +442,33 @@ try
365442
else
366443
console.error("Unsupported data type for `permissions` - use an array of strings")
367444
}
445+
// Relations with other plugins
446+
{
447+
let mentionedPlugins = new Set();
448+
if(json.hasOwnProperty("depends"))
449+
{
450+
checkPluginRelations("`depends`", json["depends"], true).forEach(item => mentionedPlugins.add(item));
451+
core.setOutput("depends", Array.from(mentionedPlugins).join(','));
452+
}
453+
if(json.hasOwnProperty("recommends"))
454+
{
455+
checkPluginRelations("`recommends`", json["recommends"], true).forEach(item => mentionedPlugins.add(item));
456+
}
457+
if(json.hasOwnProperty("suggests"))
458+
{
459+
checkPluginRelations("`suggests`", json["suggests"], true).forEach(item => mentionedPlugins.add(item));
460+
}
461+
if(json.hasOwnProperty("breaks"))
462+
{
463+
checkPluginRelations("`breaks`", json["breaks"], true).forEach(item => mentionedPlugins.add(item));
464+
}
465+
if(json.hasOwnProperty("replaces"))
466+
{
467+
checkPluginRelations("`replaces`", json["replaces"], false).forEach(item => mentionedPlugins.add(item));
468+
}
469+
470+
core.setOutput("other_plugins", Array.from(mentionedPlugins).join(','));
471+
}
368472
}
369473
}
370474
);

test/full.json

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
{
22
"api_version": 1,
33
"codename": "full_plugin",
4-
"name": "Full Plugin",
4+
"alias": [ "plugin_full", "full_example_plugin" ],
5+
"name": "Full Example Plugin",
56
"description": "Example for all available fields in Voxelite's plugin.json",
67
"version": "0.1.0",
78
"author": [ "me", "myself", "and I" ],
89
"website": "https://example.com",
10+
"depends": {
11+
"a": ">=1.0",
12+
"b": "<1.2.3"
13+
},
14+
"recommends": {
15+
"a": ">=1.1"
16+
},
17+
"suggests": {
18+
"a": ">=1.2",
19+
"c": ">2"
20+
},
21+
"breaks": {
22+
"ac": ">=1.0"
23+
},
24+
"replaces": {
25+
"zzz": "",
26+
"abc": "1.2"
27+
},
928
"permissions": [
1029
"network.http",
1130
"lua_sandbox",

0 commit comments

Comments
 (0)