Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9463df7516 | |||
| 45fab3f079 | |||
| d597b772e2 | |||
| 7abc703dbb | |||
| f9f94ecffd | |||
| c5002c79a5 | |||
| 1c720bfd18 | |||
| e55790a197 | |||
| a46e606ed0 | |||
| b6f5d3ea9f | |||
| d909647537 | |||
| a13fa85dcd | |||
| 7fbd2d120a | |||
| 9ac8c2a48e | |||
| e0fc982ae5 | |||
| c3736a0955 | |||
| 05d621a883 | |||
| 7927a80e63 | |||
| 6d02be04f7 | |||
| e27eec824f | |||
| 01def5d074 | |||
| 2f4e3cf8e3 | |||
| 2cf7d93b74 | |||
| bcc6ed5ca1 | |||
| 0c51cac769 | |||
| 47b660068e | |||
| 342c0fb72d | |||
| 5462de9d2d | |||
| ea219ded92 |
@@ -7,10 +7,10 @@ on: # rebuild any PRs and main branch changes
|
||||
- 'releases/*'
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
pre-commit-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
|
||||
@@ -19,6 +19,10 @@ jobs:
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18.17.1'
|
||||
cache: 'yarn'
|
||||
- name: Yarn Install
|
||||
run: yarn install
|
||||
- uses: actions/cache/save@v3
|
||||
@@ -27,3 +31,6 @@ jobs:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
- name: Tests
|
||||
run: yarn test
|
||||
continue-on-error: true # ignore test failures for now
|
||||
|
||||
@@ -45,6 +45,18 @@ Providing a Speckle Function ID allows you to change one of those values, and up
|
||||
|
||||
Your Speckle Token must have write permissions for the Speckle Function with this ID, otherwise the publish will fail.
|
||||
|
||||
#### `speckle_function_recommended_cpu_m`
|
||||
|
||||
*Optional.* The recommended maximum CPU in millicores for the function. If the Function exceeds this limit, it will be throttled to run within the limit.
|
||||
|
||||
1000 millicores = 1 CPU core. Defaults to 1000 millicores (1 CPU core).
|
||||
|
||||
#### `speckle_function_recommended_memory_mi`
|
||||
|
||||
*Optional.* The recommended maximum memory in mebibytes for the function. If the Function exceeds this limit, it will be **terminated**.
|
||||
|
||||
1024 mebibytes = 1 gibibyte. Defaults to 100 mebibytes.
|
||||
|
||||
### Outputs
|
||||
|
||||
#### `version_id`
|
||||
|
||||
+11
-4
@@ -21,11 +21,18 @@ inputs:
|
||||
speckle_function_command:
|
||||
description: 'The command to run to execute the function in a runtime environment.'
|
||||
required: true
|
||||
speckle_function_release_tag:
|
||||
description: 'User defined tag for the function release'
|
||||
required: true
|
||||
speckle_function_recommended_cpu_m:
|
||||
description: 'The recommended maximum CPU in millicores for the function. 1000 millicores = 1 CPU core. Defaults to 1000 millicores (1 CPU core). If the Function exceeds this limit, it will be throttled to run within the limit.'
|
||||
required: false
|
||||
speckle_function_recommended_memory_mi:
|
||||
description: 'The recommended maximum memory in mebibytes for the function. 1024 mebibytes = 1 gibibyte. Defaults to 100 mebibytes. If the Function exceeds this limit, it will be terminated.'
|
||||
required: false
|
||||
outputs:
|
||||
version_id:
|
||||
description: 'The unique identifier of the function version.'
|
||||
speckle_automate_host:
|
||||
description: 'The host component of the Speckle Automate Server URL.'
|
||||
speckle_automate_function_release_id:
|
||||
description: 'The unique identifier of the function release.'
|
||||
runs:
|
||||
using: 'node16' #FIXME bump to node18 when available
|
||||
main: 'dist/action/index.js'
|
||||
|
||||
+204
-94
@@ -554,7 +554,7 @@ class OidcClient {
|
||||
.catch(error => {
|
||||
throw new Error(`Failed to get ID Token. \n
|
||||
Error Code : ${error.statusCode}\n
|
||||
Error Message: ${error.result.message}`);
|
||||
Error Message: ${error.message}`);
|
||||
});
|
||||
const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value;
|
||||
if (!id_token) {
|
||||
@@ -8393,7 +8393,8 @@ class ParseStatus {
|
||||
status.dirty();
|
||||
if (value.status === "dirty")
|
||||
status.dirty();
|
||||
if (typeof value.value !== "undefined" || pair.alwaysSet) {
|
||||
if (key.value !== "__proto__" &&
|
||||
(typeof value.value !== "undefined" || pair.alwaysSet)) {
|
||||
finalObject[key.value] = value.value;
|
||||
}
|
||||
}
|
||||
@@ -8501,6 +8502,7 @@ class ZodType {
|
||||
this.catch = this.catch.bind(this);
|
||||
this.describe = this.describe.bind(this);
|
||||
this.pipe = this.pipe.bind(this);
|
||||
this.readonly = this.readonly.bind(this);
|
||||
this.isNullable = this.isNullable.bind(this);
|
||||
this.isOptional = this.isOptional.bind(this);
|
||||
}
|
||||
@@ -8717,6 +8719,9 @@ class ZodType {
|
||||
pipe(target) {
|
||||
return ZodPipeline.create(this, target);
|
||||
}
|
||||
readonly() {
|
||||
return ZodReadonly.create(this);
|
||||
}
|
||||
isOptional() {
|
||||
return this.safeParse(undefined).success;
|
||||
}
|
||||
@@ -8726,17 +8731,28 @@ class ZodType {
|
||||
}
|
||||
const cuidRegex = /^c[^\s-]{8,}$/i;
|
||||
const cuid2Regex = /^[a-z][a-z0-9]*$/;
|
||||
const ulidRegex = /[0-9A-HJKMNP-TV-Z]{26}/;
|
||||
const uuidRegex = /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i;
|
||||
const ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/;
|
||||
// const uuidRegex =
|
||||
// /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i;
|
||||
const uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i;
|
||||
// from https://stackoverflow.com/a/46181/1550155
|
||||
// old version: too slow, didn't support unicode
|
||||
// const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
|
||||
//old email regex
|
||||
// const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@((?!-)([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{1,})[^-<>()[\].,;:\s@"]$/i;
|
||||
// eslint-disable-next-line
|
||||
const emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/;
|
||||
// const emailRegex =
|
||||
// /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/;
|
||||
// const emailRegex =
|
||||
// /^[a-zA-Z0-9\.\!\#\$\%\&\'\*\+\/\=\?\^\_\`\{\|\}\~\-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
// const emailRegex =
|
||||
// /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i;
|
||||
const emailRegex = /^(?!\.)(?!.*\.\.)([A-Z0-9_+-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i;
|
||||
// const emailRegex =
|
||||
// /^[a-z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-z0-9-]+(?:\.[a-z0-9\-]+)*$/i;
|
||||
// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression
|
||||
const emojiRegex = /^(\p{Extended_Pictographic}|\p{Emoji_Component})+$/u;
|
||||
const _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;
|
||||
let emojiRegex;
|
||||
const ipv4Regex = /^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
|
||||
const ipv6Regex = /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;
|
||||
// Adapted from https://stackoverflow.com/a/3143231
|
||||
@@ -8776,31 +8792,6 @@ function isValidIP(ip, version) {
|
||||
return false;
|
||||
}
|
||||
class ZodString extends ZodType {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._regex = (regex, validation, message) => this.refinement((data) => regex.test(data), {
|
||||
validation,
|
||||
code: ZodIssueCode.invalid_string,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
/**
|
||||
* @deprecated Use z.string().min(1) instead.
|
||||
* @see {@link ZodString.min}
|
||||
*/
|
||||
this.nonempty = (message) => this.min(1, errorUtil.errToObj(message));
|
||||
this.trim = () => new ZodString({
|
||||
...this._def,
|
||||
checks: [...this._def.checks, { kind: "trim" }],
|
||||
});
|
||||
this.toLowerCase = () => new ZodString({
|
||||
...this._def,
|
||||
checks: [...this._def.checks, { kind: "toLowerCase" }],
|
||||
});
|
||||
this.toUpperCase = () => new ZodString({
|
||||
...this._def,
|
||||
checks: [...this._def.checks, { kind: "toUpperCase" }],
|
||||
});
|
||||
}
|
||||
_parse(input) {
|
||||
if (this._def.coerce) {
|
||||
input.data = String(input.data);
|
||||
@@ -8888,6 +8879,9 @@ class ZodString extends ZodType {
|
||||
}
|
||||
}
|
||||
else if (check.kind === "emoji") {
|
||||
if (!emojiRegex) {
|
||||
emojiRegex = new RegExp(_emojiRegex, "u");
|
||||
}
|
||||
if (!emojiRegex.test(input.data)) {
|
||||
ctx = this._getOrReturnCtx(input, ctx);
|
||||
addIssueToContext(ctx, {
|
||||
@@ -9040,6 +9034,13 @@ class ZodString extends ZodType {
|
||||
}
|
||||
return { status: status.value, value: input.data };
|
||||
}
|
||||
_regex(regex, validation, message) {
|
||||
return this.refinement((data) => regex.test(data), {
|
||||
validation,
|
||||
code: ZodIssueCode.invalid_string,
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
}
|
||||
_addCheck(check) {
|
||||
return new ZodString({
|
||||
...this._def,
|
||||
@@ -9137,6 +9138,31 @@ class ZodString extends ZodType {
|
||||
...errorUtil.errToObj(message),
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @deprecated Use z.string().min(1) instead.
|
||||
* @see {@link ZodString.min}
|
||||
*/
|
||||
nonempty(message) {
|
||||
return this.min(1, errorUtil.errToObj(message));
|
||||
}
|
||||
trim() {
|
||||
return new ZodString({
|
||||
...this._def,
|
||||
checks: [...this._def.checks, { kind: "trim" }],
|
||||
});
|
||||
}
|
||||
toLowerCase() {
|
||||
return new ZodString({
|
||||
...this._def,
|
||||
checks: [...this._def.checks, { kind: "toLowerCase" }],
|
||||
});
|
||||
}
|
||||
toUpperCase() {
|
||||
return new ZodString({
|
||||
...this._def,
|
||||
checks: [...this._def.checks, { kind: "toUpperCase" }],
|
||||
});
|
||||
}
|
||||
get isDatetime() {
|
||||
return !!this._def.checks.find((ch) => ch.kind === "datetime");
|
||||
}
|
||||
@@ -10845,6 +10871,12 @@ class ZodRecord extends ZodType {
|
||||
}
|
||||
}
|
||||
class ZodMap extends ZodType {
|
||||
get keySchema() {
|
||||
return this._def.keyType;
|
||||
}
|
||||
get valueSchema() {
|
||||
return this._def.valueType;
|
||||
}
|
||||
_parse(input) {
|
||||
const { status, ctx } = this._processInputParams(input);
|
||||
if (ctx.parsedType !== ZodParsedType.map) {
|
||||
@@ -11041,16 +11073,20 @@ class ZodFunction extends ZodType {
|
||||
const params = { errorMap: ctx.common.contextualErrorMap };
|
||||
const fn = ctx.data;
|
||||
if (this._def.returns instanceof ZodPromise) {
|
||||
return OK(async (...args) => {
|
||||
// Would love a way to avoid disabling this rule, but we need
|
||||
// an alias (using an arrow function was what caused 2651).
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const me = this;
|
||||
return OK(async function (...args) {
|
||||
const error = new ZodError([]);
|
||||
const parsedArgs = await this._def.args
|
||||
const parsedArgs = await me._def.args
|
||||
.parseAsync(args, params)
|
||||
.catch((e) => {
|
||||
error.addIssue(makeArgsIssue(args, e));
|
||||
throw error;
|
||||
});
|
||||
const result = await fn(...parsedArgs);
|
||||
const parsedReturns = await this._def.returns._def.type
|
||||
const result = await Reflect.apply(fn, this, parsedArgs);
|
||||
const parsedReturns = await me._def.returns._def.type
|
||||
.parseAsync(result, params)
|
||||
.catch((e) => {
|
||||
error.addIssue(makeReturnsIssue(result, e));
|
||||
@@ -11060,13 +11096,17 @@ class ZodFunction extends ZodType {
|
||||
});
|
||||
}
|
||||
else {
|
||||
return OK((...args) => {
|
||||
const parsedArgs = this._def.args.safeParse(args, params);
|
||||
// Would love a way to avoid disabling this rule, but we need
|
||||
// an alias (using an arrow function was what caused 2651).
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const me = this;
|
||||
return OK(function (...args) {
|
||||
const parsedArgs = me._def.args.safeParse(args, params);
|
||||
if (!parsedArgs.success) {
|
||||
throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
|
||||
}
|
||||
const result = fn(...parsedArgs.data);
|
||||
const parsedReturns = this._def.returns.safeParse(result, params);
|
||||
const result = Reflect.apply(fn, this, parsedArgs.data);
|
||||
const parsedReturns = me._def.returns.safeParse(result, params);
|
||||
if (!parsedReturns.success) {
|
||||
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
|
||||
}
|
||||
@@ -11154,7 +11194,7 @@ ZodLiteral.create = (value, params) => {
|
||||
};
|
||||
function createZodEnum(values, params) {
|
||||
return new ZodEnum({
|
||||
values: values,
|
||||
values,
|
||||
typeName: ZodFirstPartyTypeKind.ZodEnum,
|
||||
...processCreateParams(params),
|
||||
});
|
||||
@@ -11296,8 +11336,29 @@ class ZodEffects extends ZodType {
|
||||
_parse(input) {
|
||||
const { status, ctx } = this._processInputParams(input);
|
||||
const effect = this._def.effect || null;
|
||||
const checkCtx = {
|
||||
addIssue: (arg) => {
|
||||
addIssueToContext(ctx, arg);
|
||||
if (arg.fatal) {
|
||||
status.abort();
|
||||
}
|
||||
else {
|
||||
status.dirty();
|
||||
}
|
||||
},
|
||||
get path() {
|
||||
return ctx.path;
|
||||
},
|
||||
};
|
||||
checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx);
|
||||
if (effect.type === "preprocess") {
|
||||
const processed = effect.transform(ctx.data);
|
||||
const processed = effect.transform(ctx.data, checkCtx);
|
||||
if (ctx.common.issues.length) {
|
||||
return {
|
||||
status: "dirty",
|
||||
value: ctx.data,
|
||||
};
|
||||
}
|
||||
if (ctx.common.async) {
|
||||
return Promise.resolve(processed).then((processed) => {
|
||||
return this._def.schema._parseAsync({
|
||||
@@ -11315,21 +11376,6 @@ class ZodEffects extends ZodType {
|
||||
});
|
||||
}
|
||||
}
|
||||
const checkCtx = {
|
||||
addIssue: (arg) => {
|
||||
addIssueToContext(ctx, arg);
|
||||
if (arg.fatal) {
|
||||
status.abort();
|
||||
}
|
||||
else {
|
||||
status.dirty();
|
||||
}
|
||||
},
|
||||
get path() {
|
||||
return ctx.path;
|
||||
},
|
||||
};
|
||||
checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx);
|
||||
if (effect.type === "refinement") {
|
||||
const executeRefinement = (acc
|
||||
// effect: RefinementEffect<any>
|
||||
@@ -11633,8 +11679,24 @@ class ZodPipeline extends ZodType {
|
||||
});
|
||||
}
|
||||
}
|
||||
class ZodReadonly extends ZodType {
|
||||
_parse(input) {
|
||||
const result = this._def.innerType._parse(input);
|
||||
if (isValid(result)) {
|
||||
result.value = Object.freeze(result.value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
ZodReadonly.create = (type, params) => {
|
||||
return new ZodReadonly({
|
||||
innerType: type,
|
||||
typeName: ZodFirstPartyTypeKind.ZodReadonly,
|
||||
...processCreateParams(params),
|
||||
});
|
||||
};
|
||||
const custom = (check, params = {},
|
||||
/*
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Pass `fatal` into the params object instead:
|
||||
@@ -11701,6 +11763,7 @@ var ZodFirstPartyTypeKind;
|
||||
ZodFirstPartyTypeKind["ZodPromise"] = "ZodPromise";
|
||||
ZodFirstPartyTypeKind["ZodBranded"] = "ZodBranded";
|
||||
ZodFirstPartyTypeKind["ZodPipeline"] = "ZodPipeline";
|
||||
ZodFirstPartyTypeKind["ZodReadonly"] = "ZodReadonly";
|
||||
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
||||
const instanceOfType = (
|
||||
// const instanceOfType = <T extends new (...args: any[]) => any>(
|
||||
@@ -11814,6 +11877,7 @@ var z = /*#__PURE__*/Object.freeze({
|
||||
BRAND: BRAND,
|
||||
ZodBranded: ZodBranded,
|
||||
ZodPipeline: ZodPipeline,
|
||||
ZodReadonly: ZodReadonly,
|
||||
custom: custom,
|
||||
Schema: ZodType,
|
||||
ZodSchema: ZodType,
|
||||
@@ -14026,29 +14090,36 @@ var external_node_path_ = __nccwpck_require__(9411);
|
||||
|
||||
|
||||
const InputVariablesSchema = z.object({
|
||||
speckleAutomateUrl: z.string().url().nonempty(),
|
||||
speckleToken: z.string().nonempty(),
|
||||
speckleFunctionId: z.string().nonempty(),
|
||||
speckleAutomateUrl: z.string().url().min(1),
|
||||
speckleToken: z.string().min(1),
|
||||
speckleFunctionId: z.string().min(1),
|
||||
speckleFunctionInputSchema: z.record(z.string().nonempty(), z.unknown()).nullable(),
|
||||
speckleFunctionCommand: z.string().nonempty().array()
|
||||
speckleFunctionCommand: z.string().min(1).array(),
|
||||
speckleFunctionReleaseTag: z.string().max(10).min(1),
|
||||
speckleFunctionRecommendedCPUm: z.number()
|
||||
.int()
|
||||
.finite()
|
||||
.gte(100)
|
||||
.lte(16000)
|
||||
.optional(),
|
||||
speckleFunctionRecommendedMemoryMi: z.number()
|
||||
.int()
|
||||
.finite()
|
||||
.gte(1)
|
||||
.lte(8000)
|
||||
.optional()
|
||||
});
|
||||
const parseInputs = () => {
|
||||
const speckleTokenRaw = core.getInput('speckle_token', { required: true });
|
||||
core.setSecret(speckleTokenRaw);
|
||||
const rawInputSchemaPath = core.getInput('speckle_function_input_schema_file_path');
|
||||
const homeDir = process.env['HOME'];
|
||||
if (!homeDir)
|
||||
throw new Error('The home directory is not defined, cannot load inputSchema');
|
||||
let speckleFunctionInputSchema = null;
|
||||
try {
|
||||
const rawInputSchemaPath = core.getInput('speckle_function_input_schema_file_path');
|
||||
const homeDir = process.env['HOME'];
|
||||
if (!homeDir)
|
||||
throw new Error('The home directory is not defined, cannot load inputSchema');
|
||||
if (rawInputSchemaPath) {
|
||||
const rawInputSchema = (0,external_node_fs_.readFileSync)((0,external_node_path_.join)(homeDir, rawInputSchemaPath), 'utf-8');
|
||||
speckleFunctionInputSchema = JSON.parse(rawInputSchema);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
core.setFailed(`Parsing the function input schema failed with: ${err}`);
|
||||
throw err;
|
||||
if (rawInputSchemaPath) {
|
||||
const rawInputSchema = (0,external_node_fs_.readFileSync)((0,external_node_path_.join)(homeDir, rawInputSchemaPath), 'utf-8');
|
||||
speckleFunctionInputSchema = JSON.parse(rawInputSchema);
|
||||
}
|
||||
const rawInputs = {
|
||||
speckleAutomateUrl: core.getInput('speckle_automate_url', { required: true }),
|
||||
@@ -14056,40 +14127,45 @@ const parseInputs = () => {
|
||||
speckleFunctionId: core.getInput('speckle_function_id', { required: true }),
|
||||
speckleFunctionInputSchema,
|
||||
speckleFunctionCommand: core.getInput('speckle_function_command', { required: true })
|
||||
.split(' ')
|
||||
.split(' '),
|
||||
speckleFunctionReleaseTag: core.getInput('speckle_function_release_tag', {
|
||||
required: true
|
||||
}),
|
||||
speckleFunctionRecommendedCPUm: parseInt(core.getInput('speckle_function_recommended_cpu_m', {
|
||||
required: false
|
||||
})) || undefined,
|
||||
speckleFunctionRecommendedMemoryMi: parseInt(core.getInput('speckle_function_recommended_memory_mi', { required: false })) || undefined
|
||||
};
|
||||
const inputParseResult = InputVariablesSchema.safeParse(rawInputs);
|
||||
if (inputParseResult.success)
|
||||
return inputParseResult.data;
|
||||
core.setFailed(`The provided inputs do not match the required schema, ${inputParseResult.error.message}`);
|
||||
throw inputParseResult.error;
|
||||
};
|
||||
const RequiredEnvVarsSchema = z.object({
|
||||
gitRefName: z.string().nonempty(),
|
||||
gitRefType: z.string().nonempty(),
|
||||
gitCommitSha: z.string().nonempty()
|
||||
});
|
||||
const parseEnvVars = () => {
|
||||
const parseResult = RequiredEnvVarsSchema.safeParse({
|
||||
gitCommitSha: process.env.GITHUB_SHA,
|
||||
gitRefType: process.env.GITHUB_REF_TYPE,
|
||||
gitRefName: process.env.GITHUB_REF_NAME
|
||||
gitCommitSha: process.env.GITHUB_SHA
|
||||
});
|
||||
if (parseResult.success)
|
||||
return parseResult.data;
|
||||
core.setFailed(`The current execution environment does not have the required variables: ${parseResult.error.message}`);
|
||||
throw parseResult.error;
|
||||
};
|
||||
const FunctionVersionResponseBodySchema = z.object({
|
||||
versionId: z.string().nonempty()
|
||||
});
|
||||
const registerNewVersionForTheSpeckleAutomateFunction = async ({ speckleAutomateUrl, speckleFunctionCommand, speckleFunctionId, speckleFunctionInputSchema, speckleToken }, { gitCommitSha, gitRefName, gitRefType }) => {
|
||||
const registerNewVersionForTheSpeckleAutomateFunction = async ({ speckleAutomateUrl, speckleFunctionCommand, speckleFunctionId, speckleFunctionInputSchema, speckleToken, speckleFunctionReleaseTag, speckleFunctionRecommendedCPUm, speckleFunctionRecommendedMemoryMi }, commitId
|
||||
// { gitCommitSha, gitRefName, gitRefType }: RequiredEnvVars
|
||||
) => {
|
||||
try {
|
||||
const requestBody = {
|
||||
commitId: gitCommitSha,
|
||||
versionTag: gitRefType === 'tag' ? gitRefName : gitCommitSha,
|
||||
commitId,
|
||||
versionTag: speckleFunctionReleaseTag,
|
||||
command: speckleFunctionCommand,
|
||||
inputSchema: speckleFunctionInputSchema
|
||||
inputSchema: speckleFunctionInputSchema,
|
||||
recommendedCPUm: speckleFunctionRecommendedCPUm,
|
||||
recommendedMemoryMi: speckleFunctionRecommendedMemoryMi
|
||||
};
|
||||
const versionRegisterUrl = new URL(`/api/v1/functions/${speckleFunctionId}/versions`, speckleAutomateUrl);
|
||||
const retryFlag = 'RETRY THIS';
|
||||
@@ -14123,25 +14199,59 @@ const registerNewVersionForTheSpeckleAutomateFunction = async ({ speckleAutomate
|
||||
}
|
||||
}
|
||||
});
|
||||
return FunctionVersionResponseBodySchema.parse(response);
|
||||
const parsedResult = FunctionVersionResponseBodySchema.safeParse(response);
|
||||
if (parsedResult.success)
|
||||
return parsedResult.data;
|
||||
throw parsedResult.error;
|
||||
}
|
||||
catch (err) {
|
||||
core.setFailed(`Failed to register new function version to the automate server: ${err}`);
|
||||
throw err;
|
||||
throw Error('Failed to register new function version to the automate server', {
|
||||
cause: err
|
||||
});
|
||||
}
|
||||
};
|
||||
const failAndReject = async (e, errorMessageForUnknownObjectType) => {
|
||||
if (e instanceof ZodError || e instanceof Error) {
|
||||
core.setFailed(e.message);
|
||||
return Promise.reject(e.message);
|
||||
}
|
||||
core.setFailed(errorMessageForUnknownObjectType);
|
||||
return Promise.reject(e);
|
||||
};
|
||||
async function run() {
|
||||
core.info('Start registering a new version on the automate instance');
|
||||
const inputVariables = parseInputs();
|
||||
let inputVariables = {};
|
||||
try {
|
||||
inputVariables = parseInputs();
|
||||
}
|
||||
catch (e) {
|
||||
return failAndReject(e, 'Failed to parse the input variables');
|
||||
}
|
||||
core.info(`Parsed input variables to: ${JSON.stringify(inputVariables)}`);
|
||||
const requiredEnvVars = parseEnvVars();
|
||||
let requiredEnvVars = {};
|
||||
try {
|
||||
requiredEnvVars = parseEnvVars();
|
||||
}
|
||||
catch (e) {
|
||||
return failAndReject(e, 'Failed to parse the required environment variables');
|
||||
}
|
||||
const { gitCommitSha } = requiredEnvVars;
|
||||
core.info(`Parsed required environment variables to: ${JSON.stringify(requiredEnvVars)}`);
|
||||
const { speckleAutomateUrl, speckleFunctionId } = inputVariables;
|
||||
core.setOutput('speckle_automate_host', new URL(speckleAutomateUrl).host);
|
||||
core.info(`Sending a new function version definition for function ${speckleFunctionId} to the automate server: ${speckleAutomateUrl}`);
|
||||
const { versionId } = await registerNewVersionForTheSpeckleAutomateFunction(inputVariables, requiredEnvVars);
|
||||
core.setOutput('version_id', versionId);
|
||||
core.info(`Registered function version with new id: ${versionId}`);
|
||||
// github uses 7 chars to identify commits
|
||||
const commitId = gitCommitSha.substring(0, 7);
|
||||
let versionId = '';
|
||||
try {
|
||||
const registrationResponse = await registerNewVersionForTheSpeckleAutomateFunction(inputVariables, commitId);
|
||||
versionId = registrationResponse.versionId;
|
||||
}
|
||||
catch (e) {
|
||||
return failAndReject(e, 'Failed to register the new function version');
|
||||
}
|
||||
core.info(`Registered function version tagged as ${inputVariables.speckleFunctionReleaseTag} with new id: ${versionId}. Recommended CPU: ${inputVariables.speckleFunctionRecommendedCPUm}m, recommended memory: ${inputVariables.speckleFunctionRecommendedMemoryMi}Mi.`);
|
||||
core.setOutput('speckle_automate_function_release_id', versionId);
|
||||
}
|
||||
run();
|
||||
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+18
-17
@@ -16,38 +16,39 @@
|
||||
"precommit": "pre-commit run --all-files",
|
||||
"prettier:check": "prettier --check '**/*.ts'",
|
||||
"prettier:fix": "prettier --write '**/*.ts'",
|
||||
"test": "vitest --run --coverage",
|
||||
"test": "vitest --run --coverage",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.19.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/core": "^1.10.1",
|
||||
"@lifeomic/attempt": "^3.0.3",
|
||||
"node-fetch": "^3.3.2",
|
||||
"zod": "^3.21.4"
|
||||
"zod": "^3.22.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/node": "^18.17.4",
|
||||
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
||||
"@typescript-eslint/parser": "^6.3.0",
|
||||
"@types/node": "^18.18.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
"@vitest/coverage-istanbul": "^0.34.1",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.5",
|
||||
"@vitest/coverage-istanbul": "^0.34.6",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-filenames": "latest",
|
||||
"eslint-plugin-github": "^4.9.2",
|
||||
"eslint-plugin-github": "^4.10.1",
|
||||
"eslint-plugin-i18n-text": "^1.0.1",
|
||||
"eslint-plugin-import": "^2.28.0",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-no-only-tests": "^3.1.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-vitest": "^0.2.8",
|
||||
"prettier": "^3.0.1",
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.4.9",
|
||||
"vitest": "^0.34.1"
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-vitest": "^0.3.9",
|
||||
"msw": "^2.0.5",
|
||||
"prettier": "^3.0.3",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.5.0",
|
||||
"vitest": "^0.34.6"
|
||||
}
|
||||
}
|
||||
|
||||
+104
-46
@@ -1,16 +1,31 @@
|
||||
import * as core from '@actions/core'
|
||||
import { z } from 'zod'
|
||||
import { ZodError, z } from 'zod'
|
||||
import fetch from 'node-fetch'
|
||||
import { retry } from '@lifeomic/attempt'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
|
||||
const InputVariablesSchema = z.object({
|
||||
speckleAutomateUrl: z.string().url().nonempty(),
|
||||
speckleToken: z.string().nonempty(),
|
||||
speckleFunctionId: z.string().nonempty(),
|
||||
speckleAutomateUrl: z.string().url().min(1),
|
||||
speckleToken: z.string().min(1),
|
||||
speckleFunctionId: z.string().min(1),
|
||||
speckleFunctionInputSchema: z.record(z.string().nonempty(), z.unknown()).nullable(),
|
||||
speckleFunctionCommand: z.string().nonempty().array()
|
||||
speckleFunctionCommand: z.string().min(1).array(),
|
||||
speckleFunctionReleaseTag: z.string().max(10).min(1),
|
||||
speckleFunctionRecommendedCPUm: z
|
||||
.number()
|
||||
.int()
|
||||
.finite()
|
||||
.gte(100)
|
||||
.lte(16000)
|
||||
.optional(),
|
||||
speckleFunctionRecommendedMemoryMi: z
|
||||
.number()
|
||||
.int()
|
||||
.finite()
|
||||
.gte(1)
|
||||
.lte(8000)
|
||||
.optional()
|
||||
})
|
||||
|
||||
type InputVariables = z.infer<typeof InputVariablesSchema>
|
||||
@@ -19,20 +34,16 @@ const parseInputs = (): InputVariables => {
|
||||
const speckleTokenRaw = core.getInput('speckle_token', { required: true })
|
||||
core.setSecret(speckleTokenRaw)
|
||||
|
||||
const rawInputSchemaPath = core.getInput('speckle_function_input_schema_file_path')
|
||||
const homeDir = process.env['HOME']
|
||||
if (!homeDir)
|
||||
throw new Error('The home directory is not defined, cannot load inputSchema')
|
||||
let speckleFunctionInputSchema: Record<string, unknown> | null = null
|
||||
try {
|
||||
const rawInputSchemaPath = core.getInput('speckle_function_input_schema_file_path')
|
||||
const homeDir = process.env['HOME']
|
||||
if (!homeDir)
|
||||
throw new Error('The home directory is not defined, cannot load inputSchema')
|
||||
if (rawInputSchemaPath) {
|
||||
const rawInputSchema = readFileSync(join(homeDir, rawInputSchemaPath), 'utf-8')
|
||||
speckleFunctionInputSchema = JSON.parse(rawInputSchema)
|
||||
}
|
||||
} catch (err) {
|
||||
core.setFailed(`Parsing the function input schema failed with: ${err}`)
|
||||
throw err
|
||||
if (rawInputSchemaPath) {
|
||||
const rawInputSchema = readFileSync(join(homeDir, rawInputSchemaPath), 'utf-8')
|
||||
speckleFunctionInputSchema = JSON.parse(rawInputSchema)
|
||||
}
|
||||
|
||||
const rawInputs: InputVariables = {
|
||||
speckleAutomateUrl: core.getInput('speckle_automate_url', { required: true }),
|
||||
speckleToken: speckleTokenRaw,
|
||||
@@ -40,19 +51,27 @@ const parseInputs = (): InputVariables => {
|
||||
speckleFunctionInputSchema,
|
||||
speckleFunctionCommand: core
|
||||
.getInput('speckle_function_command', { required: true })
|
||||
.split(' ')
|
||||
.split(' '),
|
||||
speckleFunctionReleaseTag: core.getInput('speckle_function_release_tag', {
|
||||
required: true
|
||||
}),
|
||||
speckleFunctionRecommendedCPUm:
|
||||
parseInt(
|
||||
core.getInput('speckle_function_recommended_cpu_m', {
|
||||
required: false
|
||||
})
|
||||
) || undefined,
|
||||
speckleFunctionRecommendedMemoryMi:
|
||||
parseInt(
|
||||
core.getInput('speckle_function_recommended_memory_mi', { required: false })
|
||||
) || undefined
|
||||
}
|
||||
const inputParseResult = InputVariablesSchema.safeParse(rawInputs)
|
||||
if (inputParseResult.success) return inputParseResult.data
|
||||
core.setFailed(
|
||||
`The provided inputs do not match the required schema, ${inputParseResult.error.message}`
|
||||
)
|
||||
throw inputParseResult.error
|
||||
}
|
||||
|
||||
const RequiredEnvVarsSchema = z.object({
|
||||
gitRefName: z.string().nonempty(),
|
||||
gitRefType: z.string().nonempty(),
|
||||
gitCommitSha: z.string().nonempty()
|
||||
})
|
||||
|
||||
@@ -60,14 +79,9 @@ type RequiredEnvVars = z.infer<typeof RequiredEnvVarsSchema>
|
||||
|
||||
const parseEnvVars = (): RequiredEnvVars => {
|
||||
const parseResult = RequiredEnvVarsSchema.safeParse({
|
||||
gitCommitSha: process.env.GITHUB_SHA,
|
||||
gitRefType: process.env.GITHUB_REF_TYPE,
|
||||
gitRefName: process.env.GITHUB_REF_NAME
|
||||
gitCommitSha: process.env.GITHUB_SHA
|
||||
} as RequiredEnvVars)
|
||||
if (parseResult.success) return parseResult.data
|
||||
core.setFailed(
|
||||
`The current execution environment does not have the required variables: ${parseResult.error.message}`
|
||||
)
|
||||
throw parseResult.error
|
||||
}
|
||||
|
||||
@@ -76,6 +90,8 @@ type FunctionVersionRequestBody = {
|
||||
versionTag: string
|
||||
command: string[]
|
||||
inputSchema: Record<string, unknown> | null
|
||||
recommendedCPUm?: number
|
||||
recommendedMemoryMi?: number
|
||||
}
|
||||
|
||||
const FunctionVersionResponseBodySchema = z.object({
|
||||
@@ -90,16 +106,22 @@ const registerNewVersionForTheSpeckleAutomateFunction = async (
|
||||
speckleFunctionCommand,
|
||||
speckleFunctionId,
|
||||
speckleFunctionInputSchema,
|
||||
speckleToken
|
||||
speckleToken,
|
||||
speckleFunctionReleaseTag,
|
||||
speckleFunctionRecommendedCPUm,
|
||||
speckleFunctionRecommendedMemoryMi
|
||||
}: InputVariables,
|
||||
{ gitCommitSha, gitRefName, gitRefType }: RequiredEnvVars
|
||||
commitId: string
|
||||
// { gitCommitSha, gitRefName, gitRefType }: RequiredEnvVars
|
||||
): Promise<FunctionVersionResponseBody> => {
|
||||
try {
|
||||
const requestBody: FunctionVersionRequestBody = {
|
||||
commitId: gitCommitSha,
|
||||
versionTag: gitRefType === 'tag' ? gitRefName : gitCommitSha,
|
||||
commitId,
|
||||
versionTag: speckleFunctionReleaseTag,
|
||||
command: speckleFunctionCommand,
|
||||
inputSchema: speckleFunctionInputSchema
|
||||
inputSchema: speckleFunctionInputSchema,
|
||||
recommendedCPUm: speckleFunctionRecommendedCPUm,
|
||||
recommendedMemoryMi: speckleFunctionRecommendedMemoryMi
|
||||
}
|
||||
const versionRegisterUrl = new URL(
|
||||
`/api/v1/functions/${speckleFunctionId}/versions`,
|
||||
@@ -142,20 +164,45 @@ const registerNewVersionForTheSpeckleAutomateFunction = async (
|
||||
}
|
||||
}
|
||||
)
|
||||
return FunctionVersionResponseBodySchema.parse(response)
|
||||
const parsedResult = FunctionVersionResponseBodySchema.safeParse(response)
|
||||
if (parsedResult.success) return parsedResult.data
|
||||
throw parsedResult.error
|
||||
} catch (err) {
|
||||
core.setFailed(
|
||||
`Failed to register new function version to the automate server: ${err}`
|
||||
)
|
||||
throw err
|
||||
throw Error('Failed to register new function version to the automate server', {
|
||||
cause: err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const failAndReject = async (
|
||||
e: unknown,
|
||||
errorMessageForUnknownObjectType: string
|
||||
): Promise<never> => {
|
||||
if (e instanceof ZodError || e instanceof Error) {
|
||||
core.setFailed(e.message)
|
||||
return Promise.reject(e.message)
|
||||
}
|
||||
core.setFailed(errorMessageForUnknownObjectType)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
|
||||
export async function run(): Promise<void> {
|
||||
core.info('Start registering a new version on the automate instance')
|
||||
const inputVariables = parseInputs()
|
||||
let inputVariables: InputVariables = {} as InputVariables
|
||||
try {
|
||||
inputVariables = parseInputs()
|
||||
} catch (e: unknown) {
|
||||
return failAndReject(e, 'Failed to parse the input variables')
|
||||
}
|
||||
core.info(`Parsed input variables to: ${JSON.stringify(inputVariables)}`)
|
||||
const requiredEnvVars = parseEnvVars()
|
||||
let requiredEnvVars: RequiredEnvVars = {} as RequiredEnvVars
|
||||
try {
|
||||
requiredEnvVars = parseEnvVars()
|
||||
} catch (e: unknown) {
|
||||
return failAndReject(e, 'Failed to parse the required environment variables')
|
||||
}
|
||||
|
||||
const { gitCommitSha } = requiredEnvVars
|
||||
core.info(
|
||||
`Parsed required environment variables to: ${JSON.stringify(requiredEnvVars)}`
|
||||
)
|
||||
@@ -166,12 +213,23 @@ export async function run(): Promise<void> {
|
||||
`Sending a new function version definition for function ${speckleFunctionId} to the automate server: ${speckleAutomateUrl}`
|
||||
)
|
||||
|
||||
const { versionId } = await registerNewVersionForTheSpeckleAutomateFunction(
|
||||
inputVariables,
|
||||
requiredEnvVars
|
||||
// github uses 7 chars to identify commits
|
||||
const commitId = gitCommitSha.substring(0, 7)
|
||||
|
||||
let versionId = ''
|
||||
try {
|
||||
const registrationResponse = await registerNewVersionForTheSpeckleAutomateFunction(
|
||||
inputVariables,
|
||||
commitId
|
||||
)
|
||||
versionId = registrationResponse.versionId
|
||||
} catch (e: unknown) {
|
||||
return failAndReject(e, 'Failed to register the new function version')
|
||||
}
|
||||
core.info(
|
||||
`Registered function version tagged as ${inputVariables.speckleFunctionReleaseTag} with new id: ${versionId}. Recommended CPU: ${inputVariables.speckleFunctionRecommendedCPUm}m, recommended memory: ${inputVariables.speckleFunctionRecommendedMemoryMi}Mi.`
|
||||
)
|
||||
core.setOutput('version_id', versionId)
|
||||
core.info(`Registered function version with new id: ${versionId}`)
|
||||
core.setOutput('speckle_automate_function_release_id', versionId)
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
+207
-6
@@ -1,16 +1,217 @@
|
||||
import { run } from '../src/main.js'
|
||||
import { describe, it, vi } from 'vitest'
|
||||
import {
|
||||
describe,
|
||||
it,
|
||||
vi,
|
||||
afterEach,
|
||||
beforeEach,
|
||||
expect,
|
||||
beforeAll,
|
||||
afterAll
|
||||
} from 'vitest'
|
||||
import { mkdtempSync, writeFileSync, rmdirSync, rmSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { setupServer } from 'msw/node'
|
||||
import { http, HttpResponse } from 'msw'
|
||||
import { z } from 'zod'
|
||||
|
||||
describe('Register new version', () => {
|
||||
it('send the request', async () => {
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_ID', '{fake}')
|
||||
let tmpDir: string
|
||||
let countHappyPath = 0
|
||||
let count500Errors = 0
|
||||
|
||||
const server = setupServer(
|
||||
http.post(
|
||||
'http://myfakeautomate.speckle.internal/api/v1/functions/fake_function_id/versions',
|
||||
async ({ request }) => {
|
||||
const parseResult = FunctionVersionRequestSchema.safeParse(await request.json())
|
||||
expect(parseResult.success).to.be.true
|
||||
countHappyPath++
|
||||
return new HttpResponse(JSON.stringify({ versionId: 'fake_version_id' }), {
|
||||
status: 201,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
}
|
||||
),
|
||||
http.post(
|
||||
'http://myfakeautomate.speckle.internal/api/v1/functions/network_error/versions',
|
||||
async ({ request }) => {
|
||||
const parseResult = FunctionVersionRequestSchema.safeParse(await request.json())
|
||||
expect(parseResult.success).to.be.true
|
||||
return HttpResponse.error() // simulates a network error
|
||||
}
|
||||
),
|
||||
http.post(
|
||||
'http://myfakeautomate.speckle.internal/api/v1/functions/500_response/versions',
|
||||
async ({ request }) => {
|
||||
const parseResult = FunctionVersionRequestSchema.safeParse(await request.json())
|
||||
expect(parseResult.success).to.be.true
|
||||
count500Errors++
|
||||
return new HttpResponse(null, {
|
||||
status: 500
|
||||
})
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
|
||||
|
||||
afterAll(() => server.close())
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = mkdtempSync(join(tmpdir(), 'speckle-automate-github-action-test-'))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(tmpDir, { recursive: true })
|
||||
vi.unstubAllEnvs()
|
||||
server.resetHandlers()
|
||||
})
|
||||
|
||||
it('sends the request', async () => {
|
||||
writeFileSync(join(tmpDir, 'schema.json'), '{}')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_ID', 'fake_function_id')
|
||||
vi.stubEnv('INPUT_SPECKLE_TOKEN', '{token}')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_COMMAND', 'echo "hello automate"')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_INPUT_SCHEMA', '{}')
|
||||
vi.stubEnv('INPUT_SPECKLE_AUTOMATE_URL', 'http://automate.speckle.internal:3030')
|
||||
vi.stubEnv('HOME', tmpDir) // the input schema file path is assumed to be relative to the home directory
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_INPUT_SCHEMA_FILE_PATH', './schema.json')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_RELEASE_TAG', 'v1.0.0')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_RECOMMENDED_CPU_M', '1000')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_RECOMMENDED_MEMORY_MI', '500')
|
||||
vi.stubEnv('INPUT_SPECKLE_AUTOMATE_URL', 'http://myfakeautomate.speckle.internal')
|
||||
vi.stubEnv('GITHUB_SHA', 'commitSha')
|
||||
vi.stubEnv('GITHUB_REF_TYPE', 'commit')
|
||||
vi.stubEnv('GITHUB_REF_NAME', 'version')
|
||||
await run()
|
||||
await expect(run()).resolves.not.toThrow()
|
||||
expect(countHappyPath).to.equal(1)
|
||||
countHappyPath = 0
|
||||
})
|
||||
it('handles network errors', async () => {
|
||||
writeFileSync(join(tmpDir, 'schema.json'), '{}')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_ID', 'network_error')
|
||||
vi.stubEnv('INPUT_SPECKLE_TOKEN', '{token}')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_COMMAND', 'echo "hello automate"')
|
||||
vi.stubEnv('HOME', tmpDir) // the input schema file path is assumed to be relative to the home directory
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_INPUT_SCHEMA_FILE_PATH', './schema.json')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_RELEASE_TAG', 'v1.0.0')
|
||||
vi.stubEnv('INPUT_SPECKLE_AUTOMATE_URL', 'http://myfakeautomate.speckle.internal')
|
||||
vi.stubEnv('GITHUB_SHA', 'commitSha')
|
||||
vi.stubEnv('GITHUB_REF_TYPE', 'commit')
|
||||
vi.stubEnv('GITHUB_REF_NAME', 'version')
|
||||
await expect(run()).rejects.toThrow(
|
||||
'Failed to register new function version to the automate server'
|
||||
)
|
||||
})
|
||||
it('handles 500 responses', async () => {
|
||||
writeFileSync(join(tmpDir, 'schema.json'), '{}')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_ID', '500_response')
|
||||
vi.stubEnv('INPUT_SPECKLE_TOKEN', '{token}')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_COMMAND', 'echo "hello automate"')
|
||||
vi.stubEnv('HOME', tmpDir) // the input schema file path is assumed to be relative to the home directory
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_INPUT_SCHEMA_FILE_PATH', './schema.json')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_RELEASE_TAG', 'v1.0.0')
|
||||
vi.stubEnv('INPUT_SPECKLE_AUTOMATE_URL', 'http://myfakeautomate.speckle.internal')
|
||||
vi.stubEnv('GITHUB_SHA', 'commitSha')
|
||||
vi.stubEnv('GITHUB_REF_TYPE', 'commit')
|
||||
vi.stubEnv('GITHUB_REF_NAME', 'version')
|
||||
await expect(run()).rejects.toThrow(
|
||||
'Failed to register new function version to the automate server'
|
||||
)
|
||||
expect(count500Errors).to.toBeGreaterThan(1) // we expect the action to retry the request
|
||||
count500Errors = 0
|
||||
})
|
||||
it('errors if the token is empty', async () => {
|
||||
writeFileSync(join(tmpDir, 'schema.json'), '{}')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_ID', 'fake_function_id')
|
||||
vi.stubEnv('INPUT_SPECKLE_TOKEN', '')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_COMMAND', 'echo "hello automate"')
|
||||
vi.stubEnv('HOME', tmpDir) // the input schema file path is assumed to be relative to the home directory
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_INPUT_SCHEMA_FILE_PATH', './schema.json')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_RELEASE_TAG', 'v1.0.0')
|
||||
vi.stubEnv('INPUT_SPECKLE_AUTOMATE_URL', 'http://myfakeautomate.speckle.internal')
|
||||
vi.stubEnv('GITHUB_SHA', 'commitSha')
|
||||
vi.stubEnv('GITHUB_REF_TYPE', 'commit')
|
||||
vi.stubEnv('GITHUB_REF_NAME', 'version')
|
||||
await expect(run()).rejects.toThrow(
|
||||
'Input required and not supplied: speckle_token'
|
||||
)
|
||||
})
|
||||
it('errors if the environment variable is empty', async () => {
|
||||
writeFileSync(join(tmpDir, 'schema.json'), '{}')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_ID', 'fake_function_id')
|
||||
vi.stubEnv('INPUT_SPECKLE_TOKEN', '{token}')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_COMMAND', 'echo "hello automate"')
|
||||
vi.stubEnv('HOME', tmpDir) // the input schema file path is assumed to be relative to the home directory
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_INPUT_SCHEMA_FILE_PATH', './schema.json')
|
||||
vi.stubEnv('INPUT_SPECKLE_FUNCTION_RELEASE_TAG', 'v1.0.0')
|
||||
vi.stubEnv('INPUT_SPECKLE_AUTOMATE_URL', 'http://myfakeautomate.speckle.internal')
|
||||
vi.stubEnv('GITHUB_SHA', '')
|
||||
vi.stubEnv('GITHUB_REF_TYPE', 'commit')
|
||||
vi.stubEnv('GITHUB_REF_NAME', 'version')
|
||||
await expect(run()).rejects.toThrow('gitCommitSha')
|
||||
})
|
||||
})
|
||||
|
||||
//This must be updated to align with the schema in speckle automate
|
||||
const FunctionVersionRequestSchema = z.object({
|
||||
commitId: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(6)
|
||||
.transform((arg: string) => arg.substring(0, 10)),
|
||||
versionTag: z
|
||||
.string()
|
||||
.regex(
|
||||
new RegExp('^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$'),
|
||||
'A maximum of 128 characters are permitted. The first character must be alphanumeric (of lower or upper case) or an underscore, the subsequent characters may be alphanumeric (or lower or upper case), underscore, hyphen, or period.'
|
||||
), // regex as per OCI distribution spec https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests
|
||||
inputSchema: z.record(z.string(), z.unknown()).nullable(), // TODO: we need to validate the jsonschema somehow
|
||||
command: z.array(z.string().nonempty()),
|
||||
annotations: z
|
||||
.object({
|
||||
'speckle.systems/v1alpha1/publishing/status': z
|
||||
.enum(['publish', 'draft', 'archive'], {
|
||||
description:
|
||||
'Whether this Function is published (and should appear in the library), a draft, or archived.'
|
||||
})
|
||||
.default('draft'),
|
||||
'speckle.systems/v1alpha1/author': z
|
||||
.string({
|
||||
description:
|
||||
'The name of the authoring organization or individual of this Function.'
|
||||
})
|
||||
.optional(),
|
||||
'speckle.systems/v1alpha1/license': z
|
||||
.enum(['MIT', 'BSD', 'Apache-2.0', 'MPL', 'CC0', 'Unlicense'], {
|
||||
description:
|
||||
'The license under under which this Function is made available. This must match the license in the source code repository.'
|
||||
})
|
||||
.optional(), //TODO match the specification for license names
|
||||
'speckle.systems/v1alpha1/website': z
|
||||
.string({
|
||||
description: 'The marketing website for this Function or its authors.'
|
||||
})
|
||||
.url()
|
||||
.optional(),
|
||||
'speckle.systems/v1alpha1/documentation': z
|
||||
.string({
|
||||
description:
|
||||
'The documentation website for this function. For example, this could be a url to the README in the source code repository.'
|
||||
})
|
||||
.url()
|
||||
.optional(),
|
||||
'speckle.systems/v1alpha1/keywords': z
|
||||
.string({
|
||||
description:
|
||||
'Comma separated list of keywords used for categorizing this function.'
|
||||
})
|
||||
.optional(),
|
||||
'speckle.systems/v1alpha1/description': z.string().optional()
|
||||
})
|
||||
.optional(),
|
||||
recommendedCPUm: z.number().gte(100).lte(16000).finite().optional().default(1000),
|
||||
recommendedMemoryMi: z.number().gte(1).lte(8000).finite().optional().default(100)
|
||||
})
|
||||
|
||||
+4
-4
@@ -20,10 +20,10 @@ export default defineConfig({
|
||||
'**/*.mjs',
|
||||
'**/*.js'
|
||||
],
|
||||
lines: 95,
|
||||
functions: 95,
|
||||
branches: 95,
|
||||
statements: 95,
|
||||
lines: 90,
|
||||
functions: 90,
|
||||
branches: 70,
|
||||
statements: 90,
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src/')
|
||||
|
||||
Reference in New Issue
Block a user