@rancher/create-extension 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "NAME",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "engines": {
6
+ "node": ">=12"
7
+ },
8
+ "dependencies": {},
9
+ "resolutions": {
10
+ "**/webpack": "4",
11
+ "@types/node": "^16",
12
+ "glob": "7.2.3"
13
+ }
14
+ }
@@ -0,0 +1,16 @@
1
+ assets/fonts
2
+ coverage
3
+ .nyc_output
4
+ node_modules/
5
+ .npm
6
+ .eslintcache
7
+ .env
8
+ .cache
9
+ .next
10
+ dist
11
+ dist-pkg
12
+ .DS_Store
13
+ dynamic-importer.js
14
+ ksconfig.json
15
+ shell/utils/dynamic-importer.js
16
+ shell/assets/fonts
@@ -0,0 +1,175 @@
1
+ module.exports = {
2
+ root: true,
3
+ env: {
4
+ browser: true,
5
+ node: true
6
+ },
7
+ globals: { NodeJS: true, Timer: true },
8
+ extends: [
9
+ 'standard',
10
+ 'eslint:recommended',
11
+ 'plugin:@typescript-eslint/recommended',
12
+ '@vue/standard',
13
+ '@vue/typescript/recommended',
14
+ 'plugin:vue/recommended',
15
+ 'plugin:cypress/recommended',
16
+ ],
17
+ // add your custom rules here
18
+ rules: {
19
+ 'dot-notation': 'off',
20
+ 'generator-star-spacing': 'off',
21
+ 'guard-for-in': 'off',
22
+ 'linebreak-style': 'off',
23
+ 'new-cap': 'off',
24
+ 'no-empty': 'off',
25
+ 'no-extra-boolean-cast': 'off',
26
+ 'no-new': 'off',
27
+ 'no-plusplus': 'off',
28
+ 'no-useless-escape': 'off',
29
+ 'semi-spacing': 'off',
30
+ 'space-in-parens': 'off',
31
+ strict: 'off',
32
+ 'unicorn/no-new-buffer': 'off',
33
+ 'vue/html-self-closing': 'off',
34
+ 'vue/no-unused-components': 'warn',
35
+ 'vue/no-v-html': 'error',
36
+ 'wrap-iife': 'off',
37
+
38
+ 'array-bracket-spacing': 'warn',
39
+ 'arrow-parens': 'warn',
40
+ 'arrow-spacing': ['warn', { before: true, after: true }],
41
+ 'block-spacing': ['warn', 'always'],
42
+ 'brace-style': ['warn', '1tbs'],
43
+ 'comma-dangle': ['warn', 'only-multiline'],
44
+ 'comma-spacing': 'warn',
45
+ curly: 'warn',
46
+ eqeqeq: 'warn',
47
+ 'func-call-spacing': ['warn', 'never'],
48
+ 'implicit-arrow-linebreak': 'warn',
49
+ indent: ['warn', 2],
50
+ 'keyword-spacing': 'warn',
51
+ 'lines-between-class-members': ['warn', 'always', { exceptAfterSingleLine: true }],
52
+ 'multiline-ternary': ['warn', 'never'],
53
+ 'newline-per-chained-call': ['warn', { ignoreChainWithDepth: 4 }],
54
+ 'no-caller': 'warn',
55
+ 'no-cond-assign': ['warn', 'except-parens'],
56
+ 'no-console': 'warn',
57
+ 'no-debugger': 'warn',
58
+ 'no-eq-null': 'warn',
59
+ 'no-eval': 'warn',
60
+ 'no-trailing-spaces': 'warn',
61
+ 'no-undef': 'warn',
62
+ 'no-unused-vars': 'warn',
63
+ 'no-whitespace-before-property': 'warn',
64
+ 'object-curly-spacing': ['warn', 'always'],
65
+ 'object-property-newline': 'warn',
66
+ 'object-shorthand': 'warn',
67
+ 'padded-blocks': ['warn', 'never'],
68
+ 'prefer-arrow-callback': 'warn',
69
+ 'prefer-template': 'warn',
70
+ 'quote-props': 'warn',
71
+ 'rest-spread-spacing': 'warn',
72
+ semi: ['warn', 'always'],
73
+ 'space-before-function-paren': ['warn', 'never'],
74
+ 'space-infix-ops': 'warn',
75
+ 'spaced-comment': 'warn',
76
+ 'switch-colon-spacing': 'warn',
77
+ 'template-curly-spacing': ['warn', 'always'],
78
+ 'yield-star-spacing': ['warn', 'both'],
79
+
80
+ 'key-spacing': ['warn', {
81
+ align: {
82
+ beforeColon: false,
83
+ afterColon: true,
84
+ on: 'value',
85
+ mode: 'minimum'
86
+ },
87
+ multiLine: {
88
+ beforeColon: false,
89
+ afterColon: true
90
+ },
91
+ }],
92
+
93
+ 'object-curly-newline': ['warn', {
94
+ ObjectExpression: {
95
+ multiline: true,
96
+ minProperties: 3
97
+ },
98
+ ObjectPattern: {
99
+ multiline: true,
100
+ minProperties: 4
101
+ },
102
+ ImportDeclaration: {
103
+ multiline: true,
104
+ minProperties: 5
105
+ },
106
+ ExportDeclaration: {
107
+ multiline: true,
108
+ minProperties: 3
109
+ }
110
+ }],
111
+
112
+ 'padding-line-between-statements': [
113
+ 'warn',
114
+ {
115
+ blankLine: 'always',
116
+ prev: '*',
117
+ next: 'return',
118
+ },
119
+ {
120
+ blankLine: 'always',
121
+ prev: 'function',
122
+ next: 'function',
123
+ },
124
+ // This configuration would require blank lines after every sequence of variable declarations
125
+ {
126
+ blankLine: 'always',
127
+ prev: ['const', 'let', 'var'],
128
+ next: '*'
129
+ },
130
+ {
131
+ blankLine: 'any',
132
+ prev: ['const', 'let', 'var'],
133
+ next: ['const', 'let', 'var']
134
+ }
135
+ ],
136
+
137
+ quotes: [
138
+ 'warn',
139
+ 'single',
140
+ {
141
+ avoidEscape: true,
142
+ allowTemplateLiterals: true
143
+ },
144
+ ],
145
+
146
+ 'space-unary-ops': [
147
+ 'warn',
148
+ {
149
+ words: true,
150
+ nonwords: false,
151
+ }
152
+ ],
153
+
154
+ // FIXME: The following is disabled due to new linter and old JS code. These should all be enabled and underlying issues fixed
155
+ 'vue/order-in-components': 'off',
156
+ 'vue/no-lone-template': 'off',
157
+ 'vue/v-slot-style': 'off',
158
+ 'vue/component-tags-order': 'off',
159
+ 'vue/no-mutating-props': 'off',
160
+ '@typescript-eslint/no-unused-vars': 'off',
161
+ 'array-callback-return': 'off',
162
+ },
163
+ overrides: [
164
+ {
165
+ files: ['*.js'],
166
+ rules: {
167
+ // FIXME: The following is disabled due to new linter and old JS code. These should all be enabled and underlying issues fixed
168
+ 'prefer-regex-literals': 'off',
169
+ 'vue/component-definition-name-casing': 'off',
170
+ 'no-unreachable-loop': 'off',
171
+ 'computed-property-spacing': 'off',
172
+ }
173
+ }
174
+ ]
175
+ };
@@ -0,0 +1,24 @@
1
+ name: Build and Release Extension Catalog
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ release:
6
+ types: [released]
7
+
8
+ defaults:
9
+ run:
10
+ shell: bash
11
+ working-directory: ./
12
+
13
+ jobs:
14
+ build-extension-catalog:
15
+ uses: rancher/dashboard/.github/workflows/build-extension-catalog.yml@master
16
+ permissions:
17
+ actions: write
18
+ contents: read
19
+ packages: write
20
+ with:
21
+ registry_target: ghcr.io
22
+ registry_user: ${{ github.actor }}
23
+ secrets:
24
+ registry_token: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,22 @@
1
+ name: Build and Release Extension Charts
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ release:
6
+ types: [released]
7
+
8
+ defaults:
9
+ run:
10
+ shell: bash
11
+ working-directory: ./
12
+
13
+ jobs:
14
+ build-extension-charts:
15
+ uses: rancher/dashboard/.github/workflows/build-extension-charts.yml@master
16
+ permissions:
17
+ actions: write
18
+ contents: write
19
+ deployments: write
20
+ pages: write
21
+ with:
22
+ target_branch: gh-pages
@@ -0,0 +1,70 @@
1
+ # compiled output
2
+ /dist
3
+ /tmp
4
+ /out-tsc
5
+
6
+ # Runtime data
7
+ pids
8
+ *.pid
9
+ *.seed
10
+ *.pid.lock
11
+
12
+ # Directory for instrumented libs generated by jscoverage/JSCover
13
+ lib-cov
14
+
15
+ # Coverage directory used by tools like istanbul
16
+ coverage
17
+
18
+ # nyc test coverage
19
+ .nyc_output
20
+
21
+ # IDEs and editors
22
+ .idea
23
+ .project
24
+ .classpath
25
+ .c9/
26
+ *.launch
27
+ .settings/
28
+ *.sublime-workspace
29
+
30
+ # IDE - VSCode
31
+ .vscode/*
32
+ !.vscode/settings.json
33
+ !.vscode/tasks.json
34
+ !.vscode/launch.json
35
+ !.vscode/extensions.json
36
+
37
+ # misc
38
+ .sass-cache
39
+ connect.lock
40
+ typings
41
+
42
+ # Logs
43
+ logs
44
+ *.log
45
+ npm-debug.log*
46
+ yarn-debug.log*
47
+ yarn-error.log*
48
+
49
+ # Dependency directories
50
+ node_modules/
51
+ jspm_packages/
52
+
53
+ # Optional npm cache directory
54
+ .npm
55
+
56
+ # Optional eslint cache
57
+ .eslintcache
58
+
59
+ # Optional REPL history
60
+ .node_repl_history
61
+
62
+ # Yarn Integrity file
63
+ .yarn-integrity
64
+
65
+ # dotenv environment variables file
66
+ .env
67
+
68
+ # System Files
69
+ .DS_Store
70
+ Thumbs.db
@@ -0,0 +1,14 @@
1
+ image: registry.suse.com/bci/bci-base:latest
2
+
3
+ stages:
4
+ - check_version
5
+ - build_catalog
6
+
7
+ variables:
8
+ REGISTRY: $CI_REGISTRY
9
+ REGISTRY_USER: $CI_REGISTRY_USER
10
+ REGISTRY_PASSWORD: $CI_REGISTRY_PASSWORD
11
+ IMAGE_NAMESPACE: $CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME
12
+
13
+ include:
14
+ - remote: 'https://raw.githubusercontent.com/rancher/dashboard/master/shell/scripts/.gitlab/workflows/build-extension-catalog.gitlab-ci.yml'
@@ -0,0 +1,21 @@
1
+ {
2
+ "files.exclude": {
3
+ ".ackrc": true,
4
+ ".dockerignore": true,
5
+ ".drone.yml": true,
6
+ ".editorconfig": true,
7
+ ".eslintcache": true,
8
+ ".eslintignore": true,
9
+ ".eslintrc.js": true,
10
+ ".gitignore": true,
11
+ ".nyc_output": true,
12
+ ".vscode": true,
13
+ "LICENSE": true,
14
+ "node_modules": true,
15
+ "babel.config.js": true,
16
+ "jsconfig.json": true,
17
+ "yarn-error.log": true,
18
+ "pkg/**/.shell": true,
19
+ "pkg/**/node_modules": true,
20
+ }
21
+ }
@@ -0,0 +1 @@
1
+ module.exports = require('@rancher/shell/babel.config.js');
@@ -0,0 +1,42 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2018",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "lib": [
7
+ "ESNext",
8
+ "ESNext.AsyncIterable",
9
+ "DOM"
10
+ ],
11
+ "esModuleInterop": true,
12
+ "allowJs": true,
13
+ "sourceMap": true,
14
+ "strict": true,
15
+ "noEmit": true,
16
+ "baseUrl": ".",
17
+ "paths": {
18
+ "~/*": [
19
+ "./*"
20
+ ],
21
+ "@/*": [
22
+ "./*"
23
+ ],
24
+ "@shell/*": [
25
+ "./node_modules/@rancher/shell/*"
26
+ ]
27
+ },
28
+ "typeRoots": [
29
+ "./node_modules",
30
+ "./node_modules/@rancher/shell/types"
31
+ ],
32
+ "types": [
33
+ "@types/node",
34
+ "cypress",
35
+ "rancher",
36
+ "shell"
37
+ ]
38
+ },
39
+ "exclude": [
40
+ "node_modules"
41
+ ]
42
+ }
@@ -0,0 +1,6 @@
1
+ const config = require('@rancher/shell/vue.config'); // eslint-disable-line @typescript-eslint/no-var-requires
2
+
3
+ module.exports = config(__dirname, {
4
+ excludes: [],
5
+ // excludes: ['fleet', 'example']
6
+ });
package/app/init ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+
4
+ const { execSync } = require('child_process');
5
+ const path = require('path');
6
+ const fs = require('fs-extra');
7
+
8
+ const targets = {
9
+ dev: 'NODE_ENV=dev ./node_modules/.bin/vue-cli-service serve',
10
+ build: './node_modules/.bin/vue-cli-service build',
11
+ clean: './node_modules/@rancher/shell/scripts/clean'
12
+ };
13
+
14
+ const files = [
15
+ 'tsconfig.json',
16
+ 'vue.config.js',
17
+ '.gitignore',
18
+ '.eslintignore',
19
+ '.eslintrc.js',
20
+ 'babel.config.js',
21
+ '.vscode/settings.json'
22
+ ];
23
+
24
+ console.log('');
25
+ console.log('Creating Skeleton Application');
26
+
27
+ const args = process.argv;
28
+ let appFolder = path.resolve('.');
29
+
30
+ if ( args.length > 2 ) {
31
+ const name = args[2];
32
+ const folder = path.resolve('.');
33
+
34
+ appFolder = path.join(folder, name);
35
+ console.log(` Creating application folder: ${ appFolder }`);
36
+ fs.ensureDirSync(appFolder);
37
+ }
38
+
39
+ let addGitlabWorkflow = false;
40
+ let addWorkflowFolder = true;
41
+
42
+ // Check for Gitlab integration option
43
+ if ( args.length > 3 ) {
44
+ for (let i = 3; i < args.length; i++) {
45
+ switch (args[i]) {
46
+ case '-l':
47
+ addGitlabWorkflow = true;
48
+ break;
49
+ case '--skip-workflow':
50
+ case '-w':
51
+ addWorkflowFolder = false;
52
+ break;
53
+ default:
54
+ break;
55
+ }
56
+ }
57
+ }
58
+
59
+ if ( addGitlabWorkflow ) {
60
+ files.push('.gitlab-ci.yml');
61
+ }
62
+
63
+ let setName = false;
64
+
65
+ // Check that there is a package file
66
+ if (!fs.existsSync(path.join(appFolder, './package.json'))) {
67
+ console.log(' Adding package.json');
68
+ fs.copySync(path.join(__dirname, 'app.package.json'), path.join(appFolder, 'package.json'));
69
+ setName = true;
70
+ }
71
+
72
+ const rawdata = fs.readFileSync(path.join(appFolder, 'package.json'));
73
+ const pkg = JSON.parse(rawdata);
74
+
75
+ if (!pkg.scripts) {
76
+ pkg.scripts = {};
77
+ }
78
+
79
+ if (setName) {
80
+ const dirName = path.basename(appFolder);
81
+
82
+ pkg.name = dirName;
83
+ }
84
+
85
+ Object.keys(targets).forEach((target) => {
86
+ if (!pkg.scripts[target]) {
87
+ pkg.scripts[target] = targets[target];
88
+ console.log(` Adding script '${ target }' to package.json`);
89
+ }
90
+ });
91
+
92
+ // Add dependencies
93
+ // Use the same shell version as this app creator
94
+
95
+ const creatorPkgData = fs.readFileSync(path.join(__dirname, 'package.json'));
96
+ const creatorPkg = JSON.parse(creatorPkgData);
97
+
98
+ // Fetch the latest version of @rancher/shell
99
+ const shellVersion = execSync('npm view @rancher/shell version').toString().trim();
100
+
101
+ // Set the latest version of @rancher/shell in the dependencies
102
+ pkg.dependencies['@rancher/shell'] = `^${ shellVersion }`;
103
+
104
+ // Rest of dependencies are in the _pkgs property of package.json - copy then across
105
+ if (creatorPkg._pkgs) {
106
+ Object.keys(creatorPkg._pkgs).forEach((pkgName) => {
107
+ pkg.dependencies[pkgName] = creatorPkg._pkgs[pkgName];
108
+ });
109
+ }
110
+
111
+ fs.writeFileSync(path.join(appFolder, 'package.json'), JSON.stringify(pkg, null, 2));
112
+
113
+ // Add workflow folder if needed
114
+ if ( addWorkflowFolder ) {
115
+ // Point to the workflow directory inside the skeleton application
116
+ const workflowDir = path.join(appFolder, '.github/workflows');
117
+
118
+ if ( !fs.existsSync(workflowDir) ) {
119
+ console.log(' Creating folder: .github/workflows');
120
+ fs.mkdirSync(workflowDir, { recursive: true });
121
+ }
122
+
123
+ const workflowFiles = [
124
+ 'build-extension-catalog.yml',
125
+ 'build-extension-charts.yml'
126
+ ];
127
+
128
+ workflowFiles.forEach((fileName) => {
129
+ const dest = path.join(workflowDir, fileName); // Destination in the skeleton application
130
+ const src = path.join(__dirname, 'files/.github/workflows', fileName); // Source in the package
131
+
132
+ if ( !fs.existsSync(dest) ) {
133
+ console.log(` Adding file ${ fileName } to root workflows`);
134
+ fs.copySync(src, dest);
135
+ }
136
+ });
137
+ }
138
+
139
+ // Copy base files
140
+ files.forEach((file) => {
141
+ const src = path.join(__dirname, 'files', file);
142
+ const dest = path.join(appFolder, file);
143
+
144
+ if (!fs.existsSync(dest)) {
145
+ console.log(` Adding file: ${ file }`);
146
+
147
+ const folder = path.dirname(dest);
148
+
149
+ fs.ensureDirSync(folder);
150
+
151
+ // Create the folder if needed
152
+ fs.copySync(src, dest);
153
+ }
154
+ });
155
+
156
+ console.log('Skeleton Application creation complete.\n');
157
+
158
+ /* eslint-enable no-console */
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@rancher/create-app",
3
+ "description": "Rancher UI Application generator",
4
+ "version": "0.0.0",
5
+ "license": "Apache-2.0",
6
+ "author": "SUSE",
7
+ "private": false,
8
+ "bin": "./init",
9
+ "files": [
10
+ "**/*.*",
11
+ "init"
12
+ ],
13
+ "engines": {
14
+ "node": ">=12"
15
+ },
16
+ "_requires": [
17
+ "core-js",
18
+ "css-loader",
19
+ "@types/lodash",
20
+ "@rancher/components"
21
+ ],
22
+ "dependencies": {
23
+ "fs-extra": "^10.0.0"
24
+ }
25
+ }
package/init ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+
4
+ const { execSync } = require('child_process');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const args = process.argv.slice(2);
9
+ let extensionName = '';
10
+ let appName = '';
11
+ let updateOnly = false;
12
+ let skeletonOnly = false;
13
+ let ignoreShellDepCheck = false;
14
+
15
+ args.forEach((arg, index) => {
16
+ switch (arg) {
17
+ case '--update':
18
+ case '-u':
19
+ updateOnly = true;
20
+ break;
21
+ case '--app-name':
22
+ case '-a':
23
+ if ( args[index + 1] && !args[index + 1].startsWith('-') ) {
24
+ appName = args[index + 1];
25
+ } else {
26
+ console.error('Error: Missing value for --app-name or -a option.');
27
+ process.exit(1);
28
+ }
29
+ break;
30
+ case '--skeleton-only':
31
+ case '-s':
32
+ skeletonOnly = true;
33
+ break;
34
+ case '-i':
35
+ ignoreShellDepCheck = true;
36
+ break;
37
+ default:
38
+ if ( !arg.startsWith('-') && extensionName === '' ) {
39
+ extensionName = arg;
40
+ appName = appName || extensionName;
41
+ }
42
+ }
43
+ });
44
+
45
+ if ( !extensionName && !updateOnly && !skeletonOnly ) {
46
+ console.error('Please provide an extension name.');
47
+ process.exit(1);
48
+ }
49
+
50
+ const basePath = process.cwd();
51
+ let skeletonPath;
52
+ let isInsideSkeleton = false;
53
+ let directoryExists = false;
54
+
55
+ // Check if we are inside a skeleton application by looking for package.json
56
+ if ( fs.existsSync(path.join(basePath, 'package.json')) ) {
57
+ // Check for @rancher/shell dependency
58
+ const packageJsonPath = path.join(basePath, 'package.json');
59
+ const packageJson = require(packageJsonPath);
60
+
61
+ if ( !ignoreShellDepCheck && (!packageJson.dependencies || !packageJson.dependencies['@rancher/shell']) ) {
62
+ throw new Error('@rancher/shell dependency is missing in package.json.');
63
+ } else {
64
+ isInsideSkeleton = true;
65
+ skeletonPath = basePath;
66
+ }
67
+ } else {
68
+ // If not inside a skeleton, check if a directory with the appName already exists
69
+ skeletonPath = path.join(basePath, appName);
70
+
71
+ if ( fs.existsSync(skeletonPath) ) {
72
+ directoryExists = true;
73
+ }
74
+ }
75
+
76
+ const pkgPath = path.join(skeletonPath, 'pkg', extensionName);
77
+ const updatePath = path.join(__dirname, 'update');
78
+
79
+ const skeletonExists = fs.existsSync(skeletonPath);
80
+ const pkgExists = fs.existsSync(pkgPath);
81
+
82
+ try {
83
+ if ( updateOnly ) {
84
+ // Run the update script directly
85
+ console.log('Updating applications...');
86
+ execSync(`node ${ path.join(updatePath, 'init') }`, { stdio: 'inherit' });
87
+
88
+ console.log('Update completed successfully.');
89
+ process.exit(0);
90
+ }
91
+
92
+ // If the directory exists but we're not inside a skeleton, we should exit to prevent overwriting
93
+ if ( directoryExists && !isInsideSkeleton ) {
94
+ throw new Error(`A directory named "${ appName }" already exists. Aborting.`);
95
+ }
96
+
97
+ // Create skeleton application if it doesn't exist
98
+ if ( !isInsideSkeleton && !skeletonExists ) {
99
+ console.log(`Creating skeleton application: ${ appName }...`);
100
+ // Pass all arguments to the app/init script
101
+ execSync(`node ${ path.join(__dirname, 'app', 'init') } ${ appName } ${ args.join(' ') }`, { stdio: 'inherit' });
102
+
103
+ // Ensure the skeleton path directory is created before attempting to change directory
104
+ if ( !fs.existsSync(skeletonPath) ) {
105
+ throw new Error(`Failed to create skeleton application directory: ${ skeletonPath }`);
106
+ }
107
+
108
+ // Change working directory to the newly created skeleton app
109
+ process.chdir(skeletonPath);
110
+ } else if ( isInsideSkeleton ) {
111
+ // If skeleton exists, ensure the working directory is set correctly
112
+ process.chdir(skeletonPath);
113
+ }
114
+
115
+ if ( skeletonOnly ) {
116
+ console.log('Skeleton application created successfully. No additional packages will be installed.');
117
+ process.exit(0);
118
+ }
119
+
120
+ if ( pkgExists ) {
121
+ throw new Error(`A package directory for "${ extensionName }" already exists.`);
122
+ }
123
+
124
+ // Check for package existence and create it if necessary
125
+ if ( !pkgExists ) {
126
+ console.log(`Creating package: ${ extensionName }...`);
127
+ execSync(`node ${ path.join(__dirname, 'pkg', 'init') } ${ extensionName } ${ args.join(' ') }`, { stdio: 'inherit' });
128
+ }
129
+
130
+ if ( args.includes('--update') || args.includes('-u') ) {
131
+ // Run the update script
132
+ console.log('Updating applications...');
133
+ execSync(`node ${ path.join(updatePath, 'init') } ${ extensionName }`, { stdio: 'inherit' });
134
+ }
135
+
136
+ console.log('Extension created successfully.');
137
+
138
+ if ( skeletonOnly || !isInsideSkeleton ) {
139
+ console.log(`To begin, run: \n\n\tcd ${ appName } && yarn install\n`);
140
+ }
141
+ } catch (error) {
142
+ console.error('Error creating extension:', error);
143
+ process.exit(1);
144
+ }
145
+
146
+ /* eslint-enable no-console */
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@rancher/create-extension",
3
+ "description": "Rancher UI Extension generator",
4
+ "version": "0.1.0",
5
+ "license": "Apache-2.0",
6
+ "author": "SUSE",
7
+ "private": false,
8
+ "bin": "./init",
9
+ "files": [
10
+ "**/*",
11
+ "init"
12
+ ],
13
+ "engines": {
14
+ "node": ">=12"
15
+ },
16
+ "dependencies": {
17
+ "fs-extra": "^10.0.0"
18
+ },
19
+ "_pkgs": {
20
+ "core-js": "3.21.1",
21
+ "css-loader": "6.7.3",
22
+ "@types/lodash": "4.14.184",
23
+ "@rancher/components": "0.2.1-alpha.0"
24
+ }
25
+ }
@@ -0,0 +1 @@
1
+ module.exports = require('./.shell/pkg/babel.config.js');
@@ -0,0 +1,14 @@
1
+ import { importTypes } from '@rancher/auto-import';
2
+ import { IPlugin } from '@shell/core/types';
3
+
4
+ // Init the package
5
+ export default function(plugin: IPlugin): void {
6
+ // Auto-import model, detail, edit from the folders
7
+ importTypes(plugin);
8
+
9
+ // Provide plugin metadata from package.json
10
+ plugin.metadata = require('./package.json');
11
+
12
+ // Load a product
13
+ // plugin.addProduct(require('./product'));
14
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowJs": true,
4
+ "target": "esnext",
5
+ "module": "esnext",
6
+ "strict": true,
7
+ "jsx": "preserve",
8
+ "importHelpers": true,
9
+ "moduleResolution": "node",
10
+ "skipLibCheck": true,
11
+ "esModuleInterop": true,
12
+ "allowSyntheticDefaultImports": true,
13
+ "sourceMap": true,
14
+ "baseUrl": ".",
15
+ "preserveSymlinks": true,
16
+ "typeRoots": [
17
+ "../../node_modules",
18
+ "../../node_modules/@rancher/shell/types"
19
+ ],
20
+ "types": [
21
+ "node",
22
+ "webpack-env",
23
+ "@types/node",
24
+ "@types/jest",
25
+ "@types/lodash",
26
+ "rancher",
27
+ "shell"
28
+ ],
29
+ "lib": [
30
+ "esnext",
31
+ "dom",
32
+ "dom.iterable",
33
+ "scripthost"
34
+ ],
35
+ "paths": {
36
+ "@shell/*": [
37
+ "../../node_modules/@rancher/shell/*"
38
+ ],
39
+ "@components/*": [
40
+ "@rancher/components/*"
41
+ ]
42
+ }
43
+ },
44
+ "include": [
45
+ "**/*.ts",
46
+ "**/*.d.ts",
47
+ "**/*.tsx",
48
+ "**/*.vue"
49
+ ],
50
+ "exclude": [
51
+ "../../node_modules"
52
+ ]
53
+ }
@@ -0,0 +1 @@
1
+ module.exports = require('./.shell/pkg/vue.config')(__dirname);
package/pkg/init ADDED
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+
4
+ const { execSync } = require('child_process');
5
+ const fs = require('fs-extra');
6
+ const path = require('path');
7
+ const https = require('https');
8
+
9
+ const files = [
10
+ 'tsconfig.json',
11
+ 'vue.config.js',
12
+ 'babel.config.js',
13
+ 'index.ts'
14
+ ];
15
+
16
+ const topLevelScripts = {
17
+ 'build-pkg': './node_modules/@rancher/shell/scripts/build-pkg.sh',
18
+ 'serve-pkgs': './node_modules/@rancher/shell/scripts/serve-pkgs',
19
+ 'publish-pkgs': './node_modules/@rancher/shell/scripts/extension/publish',
20
+ 'parse-tag-name': './node_modules/@rancher/shell/scripts/extension/parse-tag-name'
21
+ };
22
+
23
+ const typeFolders = [
24
+ 'l10n',
25
+ 'models',
26
+ 'edit',
27
+ 'list',
28
+ 'detail'
29
+ ];
30
+
31
+ console.log('');
32
+ console.log('Creating Skeleton UI Package');
33
+
34
+ const args = process.argv;
35
+
36
+ const name = args[2];
37
+ const folder = path.resolve('.');
38
+ const pkgFolder = path.join(folder, 'pkg', name);
39
+
40
+ let addTypeFolders = true;
41
+
42
+ if ( args.length > 3 ) {
43
+ for ( let i = 3; i < args.length; i++ ) {
44
+ switch (args[i]) {
45
+ case '--skip-templates':
46
+ case '-t':
47
+ addTypeFolders = false;
48
+ break;
49
+ default:
50
+ break;
51
+ }
52
+ }
53
+ }
54
+
55
+ const isNodeModulesShell = !fs.existsSync(path.join(folder, 'shell'));
56
+
57
+ if (!isNodeModulesShell) {
58
+ Object.keys(topLevelScripts).forEach((script) => {
59
+ topLevelScripts[script] = topLevelScripts[script].replace('./node_modules/@rancher/shell', './shell');
60
+ });
61
+ }
62
+
63
+ console.log(` Creating package folder: ${ pkgFolder }`);
64
+
65
+ fs.ensureDirSync(pkgFolder);
66
+
67
+ console.log(' Creating package.json');
68
+ fs.copySync(path.join(__dirname, 'pkg.package.json'), path.join(pkgFolder, 'package.json'));
69
+
70
+ const rawdata = fs.readFileSync(path.join(pkgFolder, 'package.json'));
71
+ const pkg = JSON.parse(rawdata);
72
+
73
+ pkg.name = name;
74
+ pkg.description = `${ name } plugin`;
75
+
76
+ // Add annotation for the latest Rancher version by default
77
+ function fetchLatestVersion() {
78
+ console.log(' Fetching latest Rancher Version');
79
+ const options = { headers: { 'User-Agent': 'nodejs' } };
80
+
81
+ https.get('https://api.github.com/repos/rancher/rancher/releases/latest', options, (res) => {
82
+ const { statusCode } = res;
83
+ const contentType = res.headers['content-type'];
84
+
85
+ let error;
86
+
87
+ if ( statusCode !== 200 ) {
88
+ error = new Error(' Request Failed.\n' +
89
+ ` Status Code: ${ statusCode }`);
90
+ } else if ( !/^application\/json/.test(contentType) ) {
91
+ error = new Error(' Invalid content-type.\n' +
92
+ ` Expected application/json but received ${ contentType }`);
93
+ }
94
+
95
+ if ( error ) {
96
+ console.log(error.message);
97
+
98
+ res.resume();
99
+
100
+ return;
101
+ }
102
+
103
+ res.setEncoding('utf8');
104
+ let rawData = '';
105
+
106
+ res.on('data', (chunk) => {
107
+ rawData += chunk;
108
+ });
109
+ res.on('end', () => {
110
+ try {
111
+ const release = JSON.parse(rawData);
112
+
113
+ if ( release.tag_name ) {
114
+ console.log(` Adding rancher-version annotation '>= ${ release.tag_name }' to package.json`);
115
+
116
+ pkg.rancher = { annotations: { 'catalog.cattle.io/rancher-version': `>= ${ release.tag_name }` } };
117
+
118
+ // Fetch the latest version of @rancher/shell
119
+ const latestShellVersion = execSync('npm view @rancher/shell version').toString().trim() || null;
120
+
121
+ if (!latestShellVersion) {
122
+ console.log('Could not get a shell version from npm, skipping adding catalog.cattle.io/ui-extensions-version annotation to package.json');
123
+ } else {
124
+ const splitShellVersion = latestShellVersion.split('.');
125
+ const majorVersion = splitShellVersion[0];
126
+ const minorVersion = splitShellVersion[1];
127
+ const parsedShellVersion = `${ majorVersion }.${ minorVersion }.0`;
128
+
129
+ console.log(` Adding catalog.cattle.io/ui-extensions-version '>= ${ parsedShellVersion }' to package.json`);
130
+
131
+ pkg.rancher.annotations['catalog.cattle.io/ui-extensions-version'] = `>= ${ parsedShellVersion }`;
132
+ }
133
+
134
+ writePackageJson();
135
+ }
136
+ } catch (e) {
137
+ console.log(' Error parsing release data', e);
138
+ }
139
+ });
140
+ }).on('error', (e) => {
141
+ console.log(' Error fetching latest Rancher Version', e);
142
+ });
143
+ }
144
+
145
+ fetchLatestVersion();
146
+ writePackageJson();
147
+
148
+ function writePackageJson() {
149
+ fs.writeFileSync(path.join(pkgFolder, 'package.json'), JSON.stringify(pkg, null, 2));
150
+ }
151
+
152
+ // Create type folders if needed
153
+ if (addTypeFolders) {
154
+ typeFolders.forEach((dir) => {
155
+ const dest = path.join(path.join(pkgFolder, dir));
156
+
157
+ if (!fs.existsSync(dest)) {
158
+ console.log(` Creating folder: ${ dir }`);
159
+ fs.mkdirSync(dest);
160
+ }
161
+ });
162
+ }
163
+
164
+ // Copy base files
165
+ files.forEach((file) => {
166
+ const src = path.join(__dirname, 'files', file);
167
+ const dest = path.join(path.join(pkgFolder, file));
168
+
169
+ if (!fs.existsSync(dest)) {
170
+ console.log(` Adding file: ${ file }`);
171
+ fs.copySync(src, dest);
172
+ }
173
+ });
174
+
175
+ const topLevelRawdata = fs.readFileSync(path.join(folder, 'package.json'));
176
+ const topLevelPkg = JSON.parse(topLevelRawdata);
177
+ let updated = false;
178
+
179
+ Object.keys(topLevelScripts).forEach((target) => {
180
+ if (!topLevelPkg.scripts[target]) {
181
+ topLevelPkg.scripts[target] = topLevelScripts[target];
182
+ console.log(` Adding script '${ target }' to top-level package.json`);
183
+ updated = true;
184
+ }
185
+ });
186
+
187
+ if (updated) {
188
+ fs.writeFileSync(path.join(folder, 'package.json'), JSON.stringify(topLevelPkg, null, 2));
189
+ }
190
+
191
+ // Update tsconfig if needed
192
+ if (!isNodeModulesShell) {
193
+ const tsconfig = require(path.join(pkgFolder, 'tsconfig.json'));
194
+
195
+ tsconfig.include = updateArray(tsconfig.include);
196
+
197
+ Object.keys(tsconfig.compilerOptions.paths).forEach((p) => {
198
+ tsconfig.compilerOptions.paths[p] = updateArray(tsconfig.compilerOptions.paths[p]);
199
+ });
200
+
201
+ // Update typeRoots
202
+ tsconfig.compilerOptions.typeRoots = updateArray(tsconfig.compilerOptions.typeRoots);
203
+
204
+ fs.writeFileSync(path.join(pkgFolder, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
205
+ console.log(' Updated tsconfig.json for shell folder location');
206
+ }
207
+
208
+ console.log('');
209
+
210
+ function updateArray(data) {
211
+ const updated = [];
212
+
213
+ data.forEach((inc) => {
214
+ updated.push(inc.replace('../../node_modules/@rancher/shell', '../../shell'));
215
+ });
216
+
217
+ return updated;
218
+ }
219
+
220
+ /* eslint-enable no-console */
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@rancher/create-pkg",
3
+ "description": "Rancher UI Package generator",
4
+ "version": "0.0.1",
5
+ "license": "Apache-2.0",
6
+ "author": "SUSE",
7
+ "private": false,
8
+ "bin": "./init",
9
+ "files": [
10
+ "**/*.*",
11
+ "init"
12
+ ],
13
+ "engines": {
14
+ "node": ">=12"
15
+ },
16
+ "dependencies": {
17
+ "fs-extra": "^10.0.0"
18
+ }
19
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "NAME",
3
+ "description": "NAME plugin",
4
+ "version": "0.1.0",
5
+ "private": false,
6
+ "rancher": true,
7
+ "scripts": {},
8
+ "engines": {
9
+ "node": ">=12"
10
+ },
11
+ "devDependencies": {
12
+ "@vue/cli-plugin-babel": "4.5.18",
13
+ "@vue/cli-service": "4.5.18",
14
+ "@vue/cli-plugin-typescript": "4.5.18"
15
+ },
16
+ "browserslist": [
17
+ "> 1%",
18
+ "last 2 versions",
19
+ "not dead"
20
+ ]
21
+ }
@@ -0,0 +1,4 @@
1
+ declare module '*.vue' {
2
+ import Vue from 'vue';
3
+ export default Vue;
4
+ }
package/update/init ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('path');
4
+ const fs = require('fs-extra');
5
+ const { spawnSync } = require('child_process');
6
+
7
+ const scriptFolder = path.resolve(__dirname, '..');
8
+ const dest = path.resolve('.');
9
+
10
+ // Remove first two args
11
+ const args = process.argv;
12
+
13
+ args.splice(0, 2);
14
+
15
+ const res = spawnSync(path.join(__dirname, 'upgrade'), args, {
16
+ cwd: dest,
17
+ shell: false,
18
+ stdio: ['ignore', process.stdout, process.stderr],
19
+ });
20
+
21
+ if (res.status !== 0) {
22
+ process.exit(res.status);
23
+ }
24
+
25
+ // Read the existing package.json
26
+ let rawdata = fs.readFileSync(path.join(dest, 'package.json'));
27
+ const appPackage = JSON.parse(rawdata);
28
+
29
+ // Read the package.json from the app creator
30
+ rawdata = fs.readFileSync(path.join(scriptFolder, 'app', 'package.json'));
31
+ const latestPackage = JSON.parse(rawdata);
32
+
33
+ // Read the package.json from the upgrade creator
34
+ rawdata = fs.readFileSync(path.join(scriptFolder, 'package.json'));
35
+ const upgradePackage = JSON.parse(rawdata);
36
+
37
+ // Update dependency versions to match the latest from the creator
38
+ if ( latestPackage.dependencies ) {
39
+ Object.keys(latestPackage.dependencies).forEach((key) => {
40
+ appPackage.dependencies[key] = latestPackage.dependencies[key];
41
+ });
42
+ } else {
43
+ console.warn('No dependencies found in latestPackage.'); // eslint-disable-line no-console
44
+ }
45
+
46
+ // Add in the webpack resolution
47
+ appPackage.resolutions = appPackage.resolutions || {};
48
+ appPackage.resolutions['**/webpack'] = '4';
49
+
50
+ // Update the version of @rancher/shell
51
+ const shellVersion = upgradePackage.version;
52
+
53
+ appPackage.dependencies['@rancher/shell'] = shellVersion;
54
+
55
+ fs.writeFileSync(path.join(dest, 'package.json'), `${ JSON.stringify(appPackage, null, 2) }\n`);
56
+
57
+ spawnSync(`yarn`, ['install'], {
58
+ cwd: dest,
59
+ shell: false,
60
+ stdio: ['ignore', process.stdout, process.stderr],
61
+ });
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@rancher/create-update",
3
+ "description": "Rancher UI Update helper",
4
+ "version": "0.0.0",
5
+ "license": "Apache-2.0",
6
+ "author": "SUSE",
7
+ "private": false,
8
+ "bin": "./init",
9
+ "files": [
10
+ "**/*.*",
11
+ "init",
12
+ "upgrade"
13
+ ],
14
+ "engines": {
15
+ "node": ">=12"
16
+ },
17
+ "dependencies": {
18
+ "fs-extra": "^10.0.0"
19
+ }
20
+ }
package/update/upgrade ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env sh
2
+
3
+ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )"/.. &> /dev/null && pwd )
4
+
5
+ echo "Upgrading Rancher Shell"
6
+
7
+ # Get the version number from the package.json file
8
+ VERSION=$(node -p -e "require('${SCRIPT_DIR}/package.json').version")
9
+
10
+ echo "Updating to version: ${VERSION}"
11
+ echo ""
12
+
13
+ FORCE="false"
14
+
15
+ if [ "$1" == "-f" ]; then
16
+ FORCE="true"
17
+ fi
18
+
19
+ # Check for a clean git repository
20
+ if [ ! -d ".git" ] && [ "${FORCE}" == "false" ]; then
21
+ echo "Not runnning in a git repository. Re-run with -f to ignore this check"
22
+ echo "Note: This action will update your files - running in a git repository will ensure you have visibility over changes made"
23
+ exit 1
24
+ fi
25
+
26
+ if [[ $(git diff --stat) != '' ]] && [ "${FORCE}" == "false" ]; then
27
+ echo "Git repository is not clean. Re-run with -f to ignore this check"
28
+ echo "Note: This action will update your files - running in a clean git repository will ensure you have visibility over changes made"
29
+ exit 1
30
+ fi
31
+
32
+ # Check this is a Rancher Extension
33
+ if [ ! -f "./package.json" ]; then
34
+ echo "Can't find package.json - check you're running from the correct folder"
35
+ exit 1
36
+ fi
37
+
38
+ HAS_SHELL=$(grep "\"@rancher/shell\"" package.json -c )
39
+ if [ "${HAS_SHELL}" != "1" ]; then
40
+ echo "package.json does not reference @rancher/shell - check you're running from the correct folder"
41
+ exit 1
42
+ fi
43
+
44
+ # Copy files for the top-level folder (from the app creator)
45
+ rsync ${SCRIPT_DIR}/app/files/* .
46
+
47
+ # Go through each folder in the pkg folder and update their files
48
+ for pkg in ./pkg/*
49
+ do
50
+ if [ -d "${pkg}" ]; then
51
+ pkgName=$(basename $pkg)
52
+ echo "Updating package ${pkgName}"
53
+
54
+ cp ${SCRIPT_DIR}/pkg/files/* ${pkg}
55
+ fi
56
+ done