From 0a420404c6035b66706c5b5747e9f04d64a75e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 17 Feb 2022 22:54:22 +0100 Subject: [PATCH] ci: Update JS tooling Provided we use the correct version of the SpiderMonkey shell, we can perform checks using the same engine that is used by gjs. However some engine features are opt-in, so the set of features enabled by gjs and js102 may differ. The obvious option for avoiding this is replacing js102 with gjs for tests. For that reason the check-potfiles.js script has been ported to gjs and a second (simpler) script to replace `js102 --compileonly` was added. This work happened in a separate repository to make sharing between different JS-based projects easier. Update the CI image to pull in those scripts and include gjs. This will also address the issue that `js102 --compileonly` does not handle modules. Part-of: --- .gitlab-ci.yml | 24 ++-- .gitlab-ci/check-potfiles.js | 207 ----------------------------------- 2 files changed, 14 insertions(+), 217 deletions(-) delete mode 100644 .gitlab-ci/check-potfiles.js diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0fba285bc..db4a4a8b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,7 +28,6 @@ default: variables: FDO_UPSTREAM_REPO: GNOME/gnome-shell BUNDLE: "extensions-git.flatpak" - JS_LOG: "js-report.txt" LINT_LOG: "eslint-report.xml" LINT_MR_LOG: "eslint-mr-report.xml" @@ -49,13 +48,13 @@ workflow: .gnome-shell.fedora: variables: FDO_DISTRIBUTION_VERSION: 38 - FDO_DISTRIBUTION_TAG: '2023-04-20.0' + FDO_DISTRIBUTION_TAG: '2023-04-21.1' FDO_DISTRIBUTION_PACKAGES: - findutils - mozjs102-devel + gjs nodejs npm meson + pkgconfig(gobject-introspection-1.0) pkgconfig(gio-2.0) pkgconfig(gio-unix-2.0) pkgconfig(gnome-autoar-0) @@ -67,6 +66,10 @@ workflow: dnf group install -y 'Development Tools' \ 'C Development Tools and Libraries' && + ./.gitlab-ci/install-meson-project.sh \ + https://gitlab.gnome.org/fmuellner/gjs-ci-tools.git \ + main && + ./.gitlab-ci/install-meson-project.sh \ --subdir subprojects/extensions-tool/ \ --prepare ./generate-translations.sh \ @@ -126,12 +129,10 @@ js_check: - .gnome-shell.fedora stage: review script: - - find js -name '*.js' $(printf "! -wholename %s " $(cat .jscheckignore)) -exec js102 -c '{}' ';' 2>&1 | tee $JS_LOG - - (! grep -q . $JS_LOG) + - gjs-check-syntax artifacts: - paths: - - ${JS_LOG} - when: on_failure + reports: + junit: gjs-check-syntax.junit.xml eslint: extends: @@ -177,7 +178,10 @@ potfile_js_check: - .gnome-shell.fedora stage: review script: - - js102 -m .gitlab-ci/check-potfiles.js + - gjs-check-potfiles + artifacts: + reports: + junit: gjs-check-potfiles.junit.xml build: stage: build diff --git a/.gitlab-ci/check-potfiles.js b/.gitlab-ci/check-potfiles.js deleted file mode 100644 index 0c8885e25..000000000 --- a/.gitlab-ci/check-potfiles.js +++ /dev/null @@ -1,207 +0,0 @@ -const gettextFuncs = new Set([ - '_', - 'N_', - 'C_', - 'NC_', - 'dcgettext', - 'dgettext', - 'dngettext', - 'dpgettext', - 'gettext', - 'ngettext', - 'pgettext', -]); - -function dirname(file) { - const split = file.split('/'); - split.pop(); - return split.join('/'); -} - -const scriptDir = dirname(import.meta.url); -const root = dirname(scriptDir); - -const excludedFiles = new Set(); -const foundFiles = new Set() - -function addExcludes(filename) { - const contents = os.file.readFile(filename); - const lines = contents.split('\n') - .filter(l => l && !l.startsWith('#')); - lines.forEach(line => excludedFiles.add(line)); -} - -addExcludes(`${root}/po/POTFILES.in`); -addExcludes(`${root}/po/POTFILES.skip`); - -function walkAst(node, func) { - func(node); - nodesToWalk(node).forEach(n => walkAst(n, func)); -} - -function findGettextCalls(node) { - switch(node.type) { - case 'CallExpression': - if (node.callee.type === 'Identifier' && - gettextFuncs.has(node.callee.name)) - throw new Error(); - if (node.callee.type === 'MemberExpression' && - node.callee.object.type === 'Identifier' && - node.callee.object.name === 'Gettext' && - node.callee.property.type === 'Identifier' && - gettextFuncs.has(node.callee.property.name)) - throw new Error(); - break; - } - return true; -} - -function nodesToWalk(node) { - switch(node.type) { - case 'ArrayPattern': - case 'BreakStatement': - case 'CallSiteObject': // i.e. strings passed to template - case 'ContinueStatement': - case 'DebuggerStatement': - case 'EmptyStatement': - case 'Identifier': - case 'Literal': - case 'MetaProperty': // i.e. new.target - case 'Super': - case 'ThisExpression': - return []; - case 'ArrowFunctionExpression': - case 'FunctionDeclaration': - case 'FunctionExpression': - return [...node.defaults, node.body].filter(n => !!n); - case 'AssignmentExpression': - case 'BinaryExpression': - case 'ComprehensionBlock': - case 'LogicalExpression': - return [node.left, node.right]; - case 'ArrayExpression': - case 'TemplateLiteral': - return node.elements.filter(n => !!n); - case 'BlockStatement': - case 'Program': - return node.body; - case 'StaticClassBlock': - return [node.body]; - case 'ClassField': - return [node.name, node.init]; - case 'CallExpression': - case 'NewExpression': - case 'OptionalCallExpression': - case 'TaggedTemplate': - return [node.callee, ...node.arguments]; - case 'CatchClause': - return [node.body, node.guard].filter(n => !!n); - case 'ClassExpression': - case 'ClassStatement': - return [...node.body, node.superClass].filter(n => !!n); - case 'ClassMethod': - return [node.name, node.body]; - case 'ComprehensionExpression': - case 'GeneratorExpression': - return [node.body, ...node.blocks, node.filter].filter(n => !!n); - case 'ComprehensionIf': - return [node.test]; - case 'ComputedName': - return [node.name]; - case 'ConditionalExpression': - case 'IfStatement': - return [node.test, node.consequent, node.alternate].filter(n => !!n); - case 'DoWhileStatement': - case 'WhileStatement': - return [node.body, node.test]; - case 'ExportDeclaration': - return [node.declaration, node.source].filter(n => !!n); - case 'ImportDeclaration': - return [...node.specifiers, node.source]; - case 'LetStatement': - return [...node.head, node.body]; - case 'ExpressionStatement': - return [node.expression]; - case 'ForInStatement': - case 'ForOfStatement': - return [node.body, node.left, node.right]; - case 'ForStatement': - return [node.init, node.test, node.update, node.body].filter(n => !!n); - case 'LabeledStatement': - return [node.body]; - case 'MemberExpression': - return [node.object, node.property]; - case 'ObjectExpression': - case 'ObjectPattern': - return node.properties; - case 'OptionalExpression': - return [node.expression]; - case 'OptionalMemberExpression': - return [node.object, node.property]; - case 'Property': - case 'PrototypeMutation': - return [node.value]; - case 'ReturnStatement': - case 'ThrowStatement': - case 'UnaryExpression': - case 'UpdateExpression': - case 'YieldExpression': - return node.argument ? [node.argument] : []; - case 'SequenceExpression': - return node.expressions; - case 'SpreadExpression': - return [node.expression]; - case 'SwitchCase': - return [node.test, ...node.consequent].filter(n => !!n); - case 'SwitchStatement': - return [node.discriminant, ...node.cases]; - case 'TryStatement': - return [node.block, node.handler, node.finalizer].filter(n => !!n); - case 'VariableDeclaration': - return node.declarations; - case 'VariableDeclarator': - return node.init ? [node.init] : []; - case 'WithStatement': - return [node.object, node.body]; - default: - print(`Ignoring ${node.type}, you should probably fix this in the script`); - } -} - -function walkDir(dir) { - os.file.listDir(dir).forEach(child => { - if (child.startsWith('.')) - return; - - const path = os.path.join(dir, child); - const relativePath = path.replace(`${root}/`, ''); - if (excludedFiles.has(relativePath)) - return; - - if (!child.endsWith('.js')) { - try { - walkDir(path); - } catch (e) { - // not a directory - } - return; - } - - try { - const script = os.file.readFile(path); - const ast = Reflect.parse(script); - walkAst(ast, findGettextCalls); - } catch (e) { - foundFiles.add(path); - } - }); -} - -walkDir(root); - -if (foundFiles.size === 0) - quit(0); - -print('The following files are missing from po/POTFILES.in:') -foundFiles.forEach(f => print(` ${f}`)); -quit(1);