mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-25 11:21:02 +00:00
chore(deps): update dependency eslint to v9 - abandoned (#3594)
Co-authored-by: qwerty287 <qwerty287@posteo.de> Co-authored-by: qwerty287 <80460567+qwerty287@users.noreply.github.com> Co-authored-by: Anbraten <6918444+anbraten@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
c72468478d
commit
22414744b0
110 changed files with 4623 additions and 3689 deletions
|
@ -13,6 +13,7 @@
|
|||
"words": [
|
||||
"abool",
|
||||
"anbraten",
|
||||
"antfu",
|
||||
"apimachinery",
|
||||
"autoincr",
|
||||
"autoscaler",
|
||||
|
@ -67,6 +68,7 @@
|
|||
"httpsig",
|
||||
"HTTPURL",
|
||||
"httputil",
|
||||
"ianvs",
|
||||
"iconify",
|
||||
"Infof",
|
||||
"Informatyka",
|
||||
|
@ -144,6 +146,7 @@
|
|||
"tmpfs",
|
||||
"tmpl",
|
||||
"tolerations",
|
||||
"tseslint",
|
||||
"ttlcache",
|
||||
"typecheck",
|
||||
"Typeflag",
|
||||
|
|
10
.vscode/settings.json
vendored
10
.vscode/settings.json
vendored
|
@ -9,5 +9,13 @@
|
|||
"go.lintTool": "golangci-lint",
|
||||
"go.lintFlags": ["--fast"],
|
||||
"eslint.workingDirectories": ["./web"],
|
||||
"prettier.ignorePath": "./web/.prettierignore"
|
||||
"prettier.ignorePath": "./web/.prettierignore",
|
||||
// Enable the ESlint flat config support
|
||||
// (remove this if your ESLint extension above v3.0.5)
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# don't lint build output (make sure it's set to your correct build folder name)
|
||||
dist
|
||||
coverage/
|
||||
package.json
|
||||
tsconfig.eslint.json
|
||||
tsconfig.json
|
||||
src/assets/locales/
|
||||
src/assets/dayjsLocales/
|
||||
components.d.ts
|
161
web/.eslintrc.js
161
web/.eslintrc.js
|
@ -1,161 +0,0 @@
|
|||
// cSpell:ignore TSES
|
||||
// @ts-check
|
||||
/** @type {import('@typescript-eslint/experimental-utils').TSESLint.Linter.Config} */
|
||||
|
||||
/* eslint-env node */
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
reportUnusedDisableDirectives: true,
|
||||
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.eslint.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore see https://github.com/vuejs/vue-eslint-parser#parseroptionsparser
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
extraFileExtensions: ['.vue'],
|
||||
},
|
||||
|
||||
plugins: ['@typescript-eslint', 'import', 'simple-import-sort'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'airbnb-base',
|
||||
'airbnb-typescript/base',
|
||||
'plugin:import/errors',
|
||||
'plugin:import/warnings',
|
||||
'plugin:import/typescript',
|
||||
'plugin:promise/recommended',
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:vue-scoped-css/recommended',
|
||||
],
|
||||
|
||||
rules: {
|
||||
// enable scope analysis rules
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'error',
|
||||
'no-shadow': 'off',
|
||||
'@typescript-eslint/no-shadow': 'error',
|
||||
'no-redeclare': 'off',
|
||||
'@typescript-eslint/no-redeclare': 'error',
|
||||
|
||||
// make typescript eslint rules even more strict
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
||||
// SOURCE: https://github.com/iamturns/eslint-config-airbnb-typescript/blob/4aec5702be5b4e74e0e2f40bc78b4bc961681de1/lib/shared.js#L41
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'error',
|
||||
// Allow camelCase variables (23.2), PascalCase variables (23.8), and UPPER_CASE variables (23.10)
|
||||
{
|
||||
selector: 'variable',
|
||||
format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
|
||||
leadingUnderscore: 'allow',
|
||||
},
|
||||
// Allow camelCase functions (23.2), and PascalCase functions (23.8)
|
||||
{
|
||||
selector: 'function',
|
||||
format: ['camelCase', 'PascalCase'],
|
||||
},
|
||||
// Airbnb recommends PascalCase for classes (23.3), and although Airbnb does not make TypeScript recommendations, we are assuming this rule would similarly apply to anything "type like", including interfaces, type aliases, and enums
|
||||
{
|
||||
selector: 'typeLike',
|
||||
format: ['PascalCase'],
|
||||
},
|
||||
],
|
||||
|
||||
'import/no-unresolved': 'off', // disable as this is handled by tsc itself
|
||||
'import/first': 'error',
|
||||
'import/newline-after-import': 'error',
|
||||
'import/no-cycle': 'error',
|
||||
'import/no-relative-parent-imports': 'error',
|
||||
'import/no-duplicates': 'error',
|
||||
'import/no-extraneous-dependencies': 'error',
|
||||
'import/extensions': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/exports': 'error',
|
||||
|
||||
'promise/prefer-await-to-then': 'error',
|
||||
'promise/prefer-await-to-callbacks': 'error',
|
||||
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-else-return': ['error', { allowElseIf: false }],
|
||||
'no-return-assign': ['error', 'always'],
|
||||
'no-return-await': 'error',
|
||||
'no-useless-return': 'error',
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
patterns: ['src', 'dist'],
|
||||
},
|
||||
],
|
||||
'no-console': 'warn',
|
||||
'no-useless-concat': 'error',
|
||||
'prefer-const': 'error',
|
||||
'spaced-comment': ['error', 'always'],
|
||||
'object-shorthand': ['error', 'always'],
|
||||
'no-useless-rename': 'error',
|
||||
eqeqeq: 'error',
|
||||
|
||||
'vue/attribute-hyphenation': 'error',
|
||||
// enable in accordance with https://github.com/prettier/eslint-config-prettier#vuehtml-self-closing
|
||||
'vue/html-self-closing': [
|
||||
'error',
|
||||
{
|
||||
html: {
|
||||
void: 'any',
|
||||
},
|
||||
},
|
||||
],
|
||||
'vue/no-static-inline-styles': 'error',
|
||||
'vue/v-on-function-call': 'error',
|
||||
'vue/no-useless-v-bind': 'error',
|
||||
'vue/no-useless-mustaches': 'error',
|
||||
'vue/no-useless-concat': 'error',
|
||||
'vue/no-boolean-default': 'error',
|
||||
'vue/html-button-has-type': 'error',
|
||||
'vue/component-name-in-template-casing': 'error',
|
||||
'vue/match-component-file-name': [
|
||||
'error',
|
||||
{
|
||||
extensions: ['vue'],
|
||||
shouldMatchCase: true,
|
||||
},
|
||||
],
|
||||
'vue/require-name-property': 'error',
|
||||
'vue/v-for-delimiter-style': 'error',
|
||||
'vue/no-empty-component-block': 'error',
|
||||
'vue/no-duplicate-attr-inheritance': 'error',
|
||||
'vue/no-unused-properties': [
|
||||
'error',
|
||||
{
|
||||
groups: ['props', 'data', 'computed', 'methods', 'setup'],
|
||||
},
|
||||
],
|
||||
'vue/new-line-between-multi-line-property': 'error',
|
||||
'vue/padding-line-between-blocks': 'error',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/no-reserved-component-names': 'off',
|
||||
|
||||
// css rules
|
||||
'vue-scoped-css/no-unused-selector': 'error',
|
||||
'vue-scoped-css/no-parsing-error': 'error',
|
||||
'vue-scoped-css/require-scoped': 'error',
|
||||
|
||||
// enable in accordance with https://github.com/prettier/eslint-config-prettier#curly
|
||||
curly: ['error', 'all'],
|
||||
|
||||
// risky because of https://github.com/prettier/eslint-plugin-prettier#arrow-body-style-and-prefer-arrow-callback-issue
|
||||
'arrow-body-style': 'error',
|
||||
'prefer-arrow-callback': 'error',
|
||||
},
|
||||
};
|
15
web/.prettierrc.js
Normal file
15
web/.prettierrc.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
const config = JSON.parse(await readFile(new URL('../.prettierrc.json', import.meta.url)));
|
||||
|
||||
export default {
|
||||
...config,
|
||||
plugins: ['@ianvs/prettier-plugin-sort-imports'],
|
||||
importOrder: [
|
||||
'<THIRD_PARTY_MODULES>', // Imports not matched by other special words or groups.
|
||||
'', // Empty string will match any import not matched by other special words or groups.
|
||||
'^(#|@|~|\\$)(/.*)$',
|
||||
'',
|
||||
'^[./]',
|
||||
],
|
||||
};
|
119
web/eslint.config.js
Normal file
119
web/eslint.config.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
// cSpell:ignore tseslint
|
||||
// @ts-check
|
||||
|
||||
import antfu from '@antfu/eslint-config';
|
||||
import js from '@eslint/js';
|
||||
import vueI18n from '@intlify/eslint-plugin-vue-i18n';
|
||||
import eslintPluginVueScopedCSS from 'eslint-plugin-vue-scoped-css';
|
||||
|
||||
export default antfu(
|
||||
{
|
||||
stylistic: false,
|
||||
typescript: {
|
||||
tsconfigPath: './tsconfig.json',
|
||||
},
|
||||
vue: true,
|
||||
|
||||
// Disable jsonc and yaml support
|
||||
jsonc: false,
|
||||
yaml: false,
|
||||
},
|
||||
|
||||
js.configs.recommended,
|
||||
// eslintPromise.configs.recommended,
|
||||
|
||||
// TypeScript
|
||||
//...tseslint.configs.recommended,
|
||||
//...tseslint.configs.recommendedTypeChecked,
|
||||
//...tseslint.configs.strictTypeChecked,
|
||||
//...tseslint.configs.stylisticTypeChecked,
|
||||
|
||||
{
|
||||
rules: {
|
||||
'import/order': 'off',
|
||||
'sort-imports': 'off',
|
||||
},
|
||||
},
|
||||
|
||||
...eslintPluginVueScopedCSS.configs['flat/recommended'],
|
||||
|
||||
// Vue
|
||||
{
|
||||
files: ['**/*.vue'],
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/html-self-closing': [
|
||||
'error',
|
||||
{
|
||||
html: {
|
||||
void: 'always',
|
||||
normal: 'always',
|
||||
component: 'always',
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always',
|
||||
},
|
||||
],
|
||||
'vue/block-order': [
|
||||
'error',
|
||||
{
|
||||
order: ['template', 'script', 'style'],
|
||||
},
|
||||
],
|
||||
'vue/singleline-html-element-content-newline': ['off'],
|
||||
},
|
||||
},
|
||||
|
||||
// Vue I18n
|
||||
...vueI18n.configs['flat/recommended'],
|
||||
{
|
||||
rules: {
|
||||
'@intlify/vue-i18n/no-raw-text': [
|
||||
'error',
|
||||
{
|
||||
attributes: {
|
||||
'/.+/': ['label'],
|
||||
},
|
||||
},
|
||||
],
|
||||
'@intlify/vue-i18n/key-format-style': ['error', 'snake_case'],
|
||||
'@intlify/vue-i18n/no-duplicate-keys-in-locale': 'error',
|
||||
'@intlify/vue-i18n/no-dynamic-keys': 'error',
|
||||
'@intlify/vue-i18n/no-deprecated-i18n-component': 'error',
|
||||
'@intlify/vue-i18n/no-deprecated-tc': 'error',
|
||||
'@intlify/vue-i18n/no-i18n-t-path-prop': 'error',
|
||||
'@intlify/vue-i18n/no-missing-keys-in-other-locales': 'off',
|
||||
'@intlify/vue-i18n/valid-message-syntax': 'error',
|
||||
'@intlify/vue-i18n/no-missing-keys': 'error',
|
||||
'@intlify/vue-i18n/no-unknown-locale': 'error',
|
||||
'@intlify/vue-i18n/no-unused-keys': ['error', { extensions: ['.ts', '.vue'] }],
|
||||
'@intlify/vue-i18n/prefer-sfc-lang-attr': 'error',
|
||||
'@intlify/vue-i18n/no-html-messages': 'error',
|
||||
'@intlify/vue-i18n/prefer-linked-key-with-paren': 'error',
|
||||
'@intlify/vue-i18n/sfc-locale-attr': 'error',
|
||||
},
|
||||
settings: {
|
||||
'vue-i18n': {
|
||||
localeDir: './src/assets/locales/en.json',
|
||||
// Specify the version of `vue-i18n` you are using.
|
||||
// If not specified, the message will be parsed twice.
|
||||
messageSyntaxVersion: '^9.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Ignore list
|
||||
{
|
||||
ignores: [
|
||||
'dist',
|
||||
'coverage/',
|
||||
'package.json',
|
||||
'tsconfig.eslint.json',
|
||||
'tsconfig.json',
|
||||
'src/assets/locales/**/*',
|
||||
'!src/assets/locales/en.json',
|
||||
'src/assets/dayjsLocales/',
|
||||
'components.d.ts',
|
||||
],
|
||||
},
|
||||
);
|
|
@ -3,6 +3,7 @@
|
|||
"author": "Woodpecker CI",
|
||||
"version": "0.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
|
@ -10,7 +11,7 @@
|
|||
"start": "vite",
|
||||
"build": "vite build --base=/BASE_PATH",
|
||||
"serve": "vite preview",
|
||||
"lint": "eslint --max-warnings 0 --ext .js,.ts,.vue,.json .",
|
||||
"lint": "eslint --max-warnings 0 .",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier -c .",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
|
@ -18,57 +19,54 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||
"@kyvg/vue3-notification": "^3.1.3",
|
||||
"@vueuse/core": "^10.7.2",
|
||||
"@kyvg/vue3-notification": "^3.2.1",
|
||||
"@vueuse/core": "^10.10.0",
|
||||
"ansi_up": "^6.0.2",
|
||||
"dayjs": "^1.11.10",
|
||||
"dayjs": "^1.11.11",
|
||||
"fuse.js": "^7.0.0",
|
||||
"js-base64": "^3.7.6",
|
||||
"js-base64": "^3.7.7",
|
||||
"lodash": "^4.17.21",
|
||||
"node-emoji": "^2.1.3",
|
||||
"pinia": "^2.1.7",
|
||||
"prismjs": "^1.29.0",
|
||||
"semver": "^7.5.4",
|
||||
"vue": "^3.4.15",
|
||||
"vue-i18n": "^9.9.0",
|
||||
"vue-router": "^4.2.5"
|
||||
"semver": "^7.6.2",
|
||||
"vue": "^3.4.27",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-router": "^4.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.171",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "^20.11.5",
|
||||
"@types/node-emoji": "^2.0.0",
|
||||
"@types/prismjs": "^1.26.3",
|
||||
"@types/semver": "^7.5.6",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vue/compiler-sfc": "^3.4.15",
|
||||
"@vue/test-utils": "^2.4.5",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||
"eslint-plugin-vue": "^9.20.1",
|
||||
"eslint-plugin-vue-scoped-css": "^2.7.2",
|
||||
"jsdom": "^24.0.0",
|
||||
"prettier": "^3.2.4",
|
||||
"replace-in-file": "^7.1.0",
|
||||
"@antfu/eslint-config": "^2.20.0",
|
||||
"@eslint/js": "^9.4.0",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
|
||||
"@iconify/json": "^2.2.216",
|
||||
"@intlify/eslint-plugin-vue-i18n": "3.0.0-next.13",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
"@types/lodash": "^4.17.4",
|
||||
"@types/node": "^20.14.2",
|
||||
"@types/node-emoji": "^2.1.0",
|
||||
"@types/prismjs": "^1.26.4",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/tinycolor2": "^1.4.6",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vue/compiler-sfc": "^3.4.27",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"eslint": "^9.4.0",
|
||||
"eslint-plugin-promise": "^6.2.0",
|
||||
"eslint-plugin-vue-scoped-css": "^2.8.0",
|
||||
"jsdom": "^24.1.0",
|
||||
"prettier": "^3.3.0",
|
||||
"replace-in-file": "^7.2.0",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"typescript": "5.4.5",
|
||||
"unplugin-icons": "^0.18.2",
|
||||
"typescript-eslint": "^7.12.0",
|
||||
"unplugin-icons": "^0.18.5",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.0.12",
|
||||
"vite": "^5.2.12",
|
||||
"vite-plugin-prismjs": "^0.0.11",
|
||||
"vite-plugin-windicss": "^1.9.3",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vitest": "^1.5.0",
|
||||
"vue-eslint-parser": "^9.4.0",
|
||||
"vue-tsc": "^2.0.0",
|
||||
"vitest": "^1.6.0",
|
||||
"vue-tsc": "^2.0.19",
|
||||
"windicss": "^3.5.6"
|
||||
},
|
||||
"pnpm": {
|
||||
|
|
7210
web/pnpm-lock.yaml
7210
web/pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -31,7 +31,7 @@ const apiClient = useApiClient();
|
|||
const { notify } = useNotifications();
|
||||
const i18n = useI18n();
|
||||
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
// TODO reenable with eslint-plugin-promise eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
apiClient.setErrorHandler((err) => {
|
||||
if (err.status === 404) {
|
||||
notify({ title: i18n.t('errors.not_found'), type: 'error' });
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
"search": "Search…",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"url": "URL",
|
||||
"back": "Back",
|
||||
"unknown_error": "An unknown error occurred",
|
||||
"documentation_for": "Documentation for \"{topic}\"",
|
||||
|
@ -25,11 +24,6 @@
|
|||
},
|
||||
"time": {
|
||||
"template": "MMM D, YYYY, HH:mm z",
|
||||
"weeks_short": "w",
|
||||
"days_short": "d",
|
||||
"hours_short": "h",
|
||||
"min_short": "min",
|
||||
"sec_short": "sec",
|
||||
"not_started": "not started yet"
|
||||
},
|
||||
"repo": {
|
||||
|
@ -65,12 +59,10 @@
|
|||
"user_none": "This organization / user does not have any projects yet.",
|
||||
"not_allowed": "You are not allowed to access this repository",
|
||||
"enable": {
|
||||
"reload": "Reload repositories",
|
||||
"enable": "Enable",
|
||||
"enabled": "Already enabled",
|
||||
"disabled": "Disabled",
|
||||
"success": "Repository enabled",
|
||||
"list_reloaded": "Repository list reloaded"
|
||||
"success": "Repository enabled"
|
||||
},
|
||||
"open_in_forge": "Open repository in forge",
|
||||
"settings": {
|
||||
|
@ -209,7 +201,6 @@
|
|||
"tasks": "Tasks",
|
||||
"config": "Config",
|
||||
"files": "Changed files ({files})",
|
||||
"no_files": "No files have been changed.",
|
||||
"no_pipelines": "No pipelines have been started yet.",
|
||||
"no_pipeline_steps": "No pipeline steps available!",
|
||||
"step_not_started": "This step hasn't started yet.",
|
||||
|
@ -444,7 +435,6 @@
|
|||
"events": "Available at following events",
|
||||
"pr_warning": "Please be careful with this option as a bad actor can submit a malicious pull request that exposes your secrets."
|
||||
},
|
||||
"plugins_only": "Only available for plugins",
|
||||
"edit": "Edit secret",
|
||||
"delete": "Delete secret"
|
||||
},
|
||||
|
|
|
@ -147,7 +147,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction';
|
|||
import { useDate } from '~/compositions/useDate';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Agent } from '~/lib/api/types';
|
||||
import type { Agent } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
|
@ -175,14 +175,14 @@ const { doSubmit: saveAgent, isLoading: isSaving } = useAsyncAction(async () =>
|
|||
selectedAgent.value = await apiClient.createAgent(selectedAgent.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: t(isEditingAgent.value ? 'admin.settings.agents.saved' : 'admin.settings.agents.created'),
|
||||
title: isEditingAgent.value ? t('admin.settings.agents.saved') : t('admin.settings.agents.created'),
|
||||
type: 'success',
|
||||
});
|
||||
resetPage();
|
||||
});
|
||||
|
||||
const { doSubmit: deleteAgent, isLoading: isDeleting } = useAsyncAction(async (_agent: Agent) => {
|
||||
// eslint-disable-next-line no-restricted-globals, no-alert
|
||||
// eslint-disable-next-line no-alert
|
||||
if (!confirm(t('admin.settings.agents.delete_confirm'))) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="underline"
|
||||
>{{ version.latest }}</a
|
||||
>
|
||||
{{ version.latest }}
|
||||
</a>
|
||||
<span v-else>
|
||||
{{ version.latest }}
|
||||
</span>
|
||||
|
|
|
@ -43,7 +43,7 @@ import useApiClient from '~/compositions/useApiClient';
|
|||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Org } from '~/lib/api/types';
|
||||
import type { Org } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
|
@ -56,7 +56,7 @@ async function loadOrgs(page: number): Promise<Org[] | null> {
|
|||
const { resetPage, data: orgs } = usePagination(loadOrgs);
|
||||
|
||||
const { doSubmit: deleteOrg, isLoading: isDeleting } = useAsyncAction(async (_org: Org) => {
|
||||
// eslint-disable-next-line no-restricted-globals, no-alert
|
||||
// eslint-disable-next-line no-alert
|
||||
if (!confirm(t('admin.settings.orgs.delete_confirm'))) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ import ListItem from '~/components/atomic/ListItem.vue';
|
|||
import Settings from '~/components/layout/Settings.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { QueueInfo } from '~/lib/api/types/queue';
|
||||
import type { QueueInfo } from '~/lib/api/types';
|
||||
|
||||
import AdminQueueStats from './queue/AdminQueueStats.vue';
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</div>
|
||||
</ListItem>
|
||||
|
||||
<div v-if="repos?.length === 0" class="ml-2">{{ $t('admin.settings.orgs.none') }}</div>
|
||||
<div v-if="repos?.length === 0" class="ml-2">{{ $t('admin.settings.repos.none') }}</div>
|
||||
</div>
|
||||
</Settings>
|
||||
</template>
|
||||
|
@ -49,7 +49,7 @@ import useApiClient from '~/compositions/useApiClient';
|
|||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Repo } from '~/lib/api/types';
|
||||
import type { Repo } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
|
|
|
@ -41,7 +41,8 @@ import useApiClient from '~/compositions/useApiClient';
|
|||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Secret, WebhookEvents } from '~/lib/api/types';
|
||||
import type { Secret } from '~/lib/api/types';
|
||||
import { WebhookEvents } from '~/lib/api/types';
|
||||
|
||||
const emptySecret: Partial<Secret> = {
|
||||
name: '',
|
||||
|
@ -74,7 +75,7 @@ const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async ()
|
|||
await apiClient.createGlobalSecret(selectedSecret.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(isEditingSecret.value ? 'secrets.saved' : 'secrets.created'),
|
||||
title: isEditingSecret.value ? i18n.t('secrets.saved') : i18n.t('secrets.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedSecret.value = undefined;
|
||||
|
|
|
@ -97,7 +97,7 @@ import useApiClient from '~/compositions/useApiClient';
|
|||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { User } from '~/lib/api/types';
|
||||
import type { User } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
|
@ -135,7 +135,7 @@ const { doSubmit: saveUser, isLoading: isSaving } = useAsyncAction(async () => {
|
|||
});
|
||||
|
||||
const { doSubmit: deleteUser, isLoading: isDeleting } = useAsyncAction(async (_user: User) => {
|
||||
// eslint-disable-next-line no-restricted-globals, no-alert
|
||||
// eslint-disable-next-line no-alert
|
||||
if (!confirm(t('admin.settings.users.delete_confirm'))) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -54,14 +54,14 @@
|
|||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { QueueStats } from '~/lib/api/types/queue';
|
||||
|
||||
const { t } = useI18n();
|
||||
import type { QueueStats } from '~/lib/api/types/queue';
|
||||
|
||||
const props = defineProps<{
|
||||
stats?: QueueStats;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const total = computed(() => {
|
||||
if (!props.stats) {
|
||||
return 0;
|
||||
|
|
|
@ -36,9 +36,10 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, useAttrs } from 'vue';
|
||||
import { RouteLocationRaw } from 'vue-router';
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
|
||||
import Icon, { IconNames } from '~/components/atomic/Icon.vue';
|
||||
import type { IconNames } from '~/components/atomic/Icon.vue';
|
||||
import Icon from '~/components/atomic/Icon.vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<template>
|
||||
<a
|
||||
:href="`${docsUrl}`"
|
||||
:title="$t('documentation_for', { topic: topic })"
|
||||
:title="$t('documentation_for', { topic })"
|
||||
target="_blank"
|
||||
class="text-wp-link-100 hover:text-wp-link-200 cursor-pointer mt-1"
|
||||
><Icon name="question" class="!w-4 !h-4"
|
||||
/></a>
|
||||
>
|
||||
<Icon name="question" class="!w-4 !h-4" />
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { RouteLocationRaw } from 'vue-router';
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
|
||||
import Icon, { IconNames } from '~/components/atomic/Icon.vue';
|
||||
import Icon, { type IconNames } from '~/components/atomic/Icon.vue';
|
||||
|
||||
defineProps<{
|
||||
icon?: IconNames;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { RouteLocationRaw } from 'vue-router';
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
|
||||
defineProps<{
|
||||
clickable?: boolean;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import '~/style/prism.css';
|
||||
|
||||
import Prism from 'prismjs';
|
||||
import { computed, defineComponent, h, toRef, VNode } from 'vue';
|
||||
import { computed, defineComponent, h, toRef, type VNode } from 'vue';
|
||||
|
||||
declare type Data = Record<string, unknown>;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
import { computed, toRef } from 'vue';
|
||||
|
||||
import Checkbox from './Checkbox.vue';
|
||||
import { CheckboxOption } from './form.types';
|
||||
import type { CheckboxOption } from './form.types';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: CheckboxOption['value'][];
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<div class="flex items-center mb-2">
|
||||
<label class="text-wp-text-100 font-bold" :for="id" v-bind="$attrs">{{ label }}</label>
|
||||
<DocsLink v-if="docsUrl" :topic="label" :url="docsUrl" class="ml-2" />
|
||||
<slot v-else-if="$slots['titleActions']" name="titleActions" />
|
||||
<slot v-else-if="$slots.titleActions" name="titleActions" />
|
||||
</div>
|
||||
<slot :id="id" />
|
||||
<div v-if="$slots['description']" class="ml-1 text-wp-text-alt-100">
|
||||
<div v-if="$slots.description" class="ml-1 text-wp-text-alt-100">
|
||||
<slot name="description" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,7 +20,7 @@ const modelValue = toRef(props, 'modelValue');
|
|||
const innerValue = computed({
|
||||
get: () => modelValue.value.toString(),
|
||||
set: (value) => {
|
||||
emit('update:modelValue', parseFloat(value));
|
||||
emit('update:modelValue', Number.parseFloat(value));
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
import { RadioOption } from './form.types';
|
||||
import type { RadioOption } from './form.types';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
import { SelectOption } from './form.types';
|
||||
import type { SelectOption } from './form.types';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
export type SelectOption = {
|
||||
export interface SelectOption {
|
||||
value: string;
|
||||
text: string;
|
||||
description?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type RadioOption = SelectOption;
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { IconNames } from '~/components/atomic/Icon.vue';
|
||||
import { Tab, useTabsClient } from '~/compositions/useTabs';
|
||||
import type { IconNames } from '~/components/atomic/Icon.vue';
|
||||
import { useTabsClient, type Tab } from '~/compositions/useTabs';
|
||||
|
||||
const props = defineProps<{
|
||||
id?: string;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { Tab, useTabsClient } from '~/compositions/useTabs';
|
||||
import { useTabsClient, type Tab } from '~/compositions/useTabs';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { computed, inject, Ref, ref } from 'vue';
|
||||
import { computed, inject, ref, type Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
|
@ -36,7 +36,7 @@ import useApiClient from '~/compositions/useApiClient';
|
|||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Org, Secret, WebhookEvents } from '~/lib/api/types';
|
||||
import { WebhookEvents, type Org, type Secret } from '~/lib/api/types';
|
||||
|
||||
const emptySecret: Partial<Secret> = {
|
||||
name: '',
|
||||
|
@ -78,7 +78,7 @@ const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async ()
|
|||
await apiClient.createOrgSecret(org.value.id, selectedSecret.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(isEditingSecret.value ? 'secrets.saved' : 'secrets.created'),
|
||||
title: isEditingSecret.value ? i18n.t('secrets.saved') : i18n.t('secrets.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedSecret.value = undefined;
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
params: { repoId: pipeline.repo_id },
|
||||
}"
|
||||
class="underline"
|
||||
>{{ repo?.owner }} / {{ repo?.name }}</router-link
|
||||
>
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
{{ repo?.owner }} / {{ repo?.name }}
|
||||
</router-link>
|
||||
<span class="whitespace-nowrap overflow-hidden overflow-ellipsis" :title="message">{{ title }}</span>
|
||||
<div class="flex flex-col mt-2">
|
||||
<div class="flex space-x-2 items-center" :title="created">
|
||||
|
@ -31,7 +33,7 @@ import { computed, toRef } from 'vue';
|
|||
import Icon from '~/components/atomic/Icon.vue';
|
||||
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
||||
import usePipeline from '~/compositions/usePipeline';
|
||||
import { PipelineFeed } from '~/lib/api/types';
|
||||
import type { PipelineFeed } from '~/lib/api/types';
|
||||
import { useRepoStore } from '~/store/repos';
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
|
@ -24,13 +24,16 @@
|
|||
</div>
|
||||
|
||||
<div class="w-full md:w-auto md:mx-4 flex items-center min-w-0">
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
<span class="text-wp-text-alt-100 <md:hidden">#{{ pipeline.number }}</span>
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
<span class="text-wp-text-alt-100 <md:hidden mx-2">-</span>
|
||||
<span
|
||||
class="text-wp-text-100 <md:underline whitespace-nowrap overflow-hidden overflow-ellipsis"
|
||||
:title="message"
|
||||
>{{ title }}</span
|
||||
>
|
||||
{{ title }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
@ -75,7 +78,7 @@ import { pipelineStatusColors } from '~/components/repo/pipeline/pipeline-status
|
|||
import PipelineRunningIcon from '~/components/repo/pipeline/PipelineRunningIcon.vue';
|
||||
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
||||
import usePipeline from '~/compositions/usePipeline';
|
||||
import { Pipeline } from '~/lib/api/types';
|
||||
import type { Pipeline } from '~/lib/api/types';
|
||||
|
||||
const props = defineProps<{
|
||||
pipeline: Pipeline;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<script lang="ts" setup>
|
||||
import Panel from '~/components/layout/Panel.vue';
|
||||
import PipelineItem from '~/components/repo/pipeline/PipelineItem.vue';
|
||||
import { Pipeline } from '~/lib/api/types';
|
||||
import type { Pipeline } from '~/lib/api/types';
|
||||
|
||||
defineProps<{
|
||||
pipelines: Pipeline[] | undefined;
|
||||
|
|
|
@ -60,8 +60,9 @@
|
|||
'bg-opacity-30 bg-blue-600': isSelected(line),
|
||||
underline: isSelected(line),
|
||||
}"
|
||||
>{{ line.number }}</a
|
||||
>
|
||||
{{ line.number }}
|
||||
</a>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<span
|
||||
class="align-top whitespace-pre-wrap break-words"
|
||||
|
@ -80,8 +81,9 @@
|
|||
'bg-opacity-40 dark:bg-opacity-50 bg-yellow-600 dark:bg-yellow-800': line.type === 'warning',
|
||||
'bg-opacity-30 bg-blue-600': isSelected(line),
|
||||
}"
|
||||
>{{ formatTime(line.time) }}</span
|
||||
>
|
||||
{{ formatTime(line.time) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -110,7 +112,7 @@ import { useStorage } from '@vueuse/core';
|
|||
import { AnsiUp } from 'ansi_up';
|
||||
import { decode } from 'js-base64';
|
||||
import { debounce } from 'lodash';
|
||||
import { computed, inject, nextTick, onMounted, Ref, ref, toRef, watch } from 'vue';
|
||||
import { computed, inject, nextTick, onMounted, ref, toRef, watch, type Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
|
@ -118,16 +120,16 @@ import IconButton from '~/components/atomic/IconButton.vue';
|
|||
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
import type { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
import { findStep, isStepFinished, isStepRunning } from '~/utils/helpers';
|
||||
|
||||
type LogLine = {
|
||||
interface LogLine {
|
||||
index: number;
|
||||
number: number;
|
||||
text: string;
|
||||
time?: number;
|
||||
type: 'error' | 'warning' | null;
|
||||
};
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
pipeline: Pipeline;
|
||||
|
@ -246,7 +248,7 @@ async function download() {
|
|||
downloadInProgress.value = true;
|
||||
logs = await apiClient.getLogs(repo.value.id, pipeline.value.number, step.value.id);
|
||||
} catch (e) {
|
||||
notifications.notifyError(e, i18n.t('repo.pipeline.log_download_error'));
|
||||
notifications.notifyError(e as Error, i18n.t('repo.pipeline.log_download_error'));
|
||||
return;
|
||||
} finally {
|
||||
downloadInProgress.value = false;
|
||||
|
@ -312,7 +314,7 @@ async function deleteLogs() {
|
|||
}
|
||||
|
||||
// TODO: use proper dialog (copy-pasted from web/src/components/secrets/SecretList.vue:deleteSecret)
|
||||
// eslint-disable-next-line no-alert, no-restricted-globals
|
||||
// eslint-disable-next-line no-alert
|
||||
if (!confirm(i18n.t('repo.pipeline.log_delete_confirm'))) {
|
||||
return;
|
||||
}
|
||||
|
@ -321,7 +323,7 @@ async function deleteLogs() {
|
|||
await apiClient.deleteLogs(repo.value.id, pipeline.value.number, step.value.id);
|
||||
log.value = [];
|
||||
} catch (e) {
|
||||
notifications.notifyError(e, i18n.t('repo.pipeline.log_delete_error'));
|
||||
notifications.notifyError(e as Error, i18n.t('repo.pipeline.log_delete_error'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
class="flex items-center justify-center"
|
||||
:title="$t('repo.pipeline.status.status', { status: $t(`repo.pipeline.status.${status}`) })"
|
||||
:title="$t('repo.pipeline.status.status', { status: statusDescriptions[status] })"
|
||||
>
|
||||
<Icon
|
||||
:name="service ? 'settings' : `status-${status}`"
|
||||
|
@ -18,8 +18,10 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Icon from '~/components/atomic/Icon.vue';
|
||||
import { PipelineStatus } from '~/lib/api/types';
|
||||
import type { PipelineStatus } from '~/lib/api/types';
|
||||
|
||||
import { pipelineStatusColors } from './pipeline-status';
|
||||
|
||||
|
@ -27,4 +29,22 @@ defineProps<{
|
|||
status: PipelineStatus;
|
||||
service?: boolean;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const statusDescriptions = {
|
||||
blocked: t('repo.pipeline.status.blocked'),
|
||||
declined: t('repo.pipeline.status.declined'),
|
||||
error: t('repo.pipeline.status.error'),
|
||||
failure: t('repo.pipeline.status.failure'),
|
||||
killed: t('repo.pipeline.status.killed'),
|
||||
pending: t('repo.pipeline.status.pending'),
|
||||
running: t('repo.pipeline.status.running'),
|
||||
skipped: t('repo.pipeline.status.skipped'),
|
||||
started: t('repo.pipeline.status.started'),
|
||||
success: t('repo.pipeline.status.success'),
|
||||
} satisfies {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
[_ in PipelineStatus]: string;
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -7,7 +7,7 @@ import { computed, toRef } from 'vue';
|
|||
|
||||
import { useDate } from '~/compositions/useDate';
|
||||
import { useElapsedTime } from '~/compositions/useElapsedTime';
|
||||
import { PipelineStep, PipelineWorkflow } from '~/lib/api/types';
|
||||
import type { PipelineStep, PipelineWorkflow } from '~/lib/api/types';
|
||||
|
||||
const props = defineProps<{
|
||||
step?: PipelineStep;
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, ref, toRef } from 'vue';
|
||||
import { computed, inject, ref, toRef, type Ref } from 'vue';
|
||||
|
||||
import Badge from '~/components/atomic/Badge.vue';
|
||||
import Icon from '~/components/atomic/Icon.vue';
|
||||
|
@ -127,7 +127,7 @@ import Panel from '~/components/layout/Panel.vue';
|
|||
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
||||
import PipelineStepDuration from '~/components/repo/pipeline/PipelineStepDuration.vue';
|
||||
import usePipeline from '~/compositions/usePipeline';
|
||||
import { Pipeline, PipelineConfig, PipelineStep, StepType } from '~/lib/api/types';
|
||||
import { StepType, type Pipeline, type PipelineConfig, type PipelineStep } from '~/lib/api/types';
|
||||
|
||||
const props = defineProps<{
|
||||
pipeline: Pipeline;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { PipelineStatus } from '~/lib/api/types';
|
||||
import type { PipelineStatus } from '~/lib/api/types';
|
||||
|
||||
export const pipelineStatusColors: Record<PipelineStatus, 'green' | 'gray' | 'red' | 'blue' | 'orange'> = {
|
||||
blocked: 'gray',
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref } from 'vue';
|
||||
import { computed, inject, type Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
|
@ -51,7 +51,7 @@ import Settings from '~/components/layout/Settings.vue';
|
|||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { Repo } from '~/lib/api/types';
|
||||
import type { Repo } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const router = useRouter();
|
||||
|
@ -75,7 +75,7 @@ const { doSubmit: deleteRepo, isLoading: isDeletingRepo } = useAsyncAction(async
|
|||
}
|
||||
|
||||
// TODO: use proper dialog
|
||||
// eslint-disable-next-line no-alert, no-restricted-globals
|
||||
// eslint-disable-next-line no-alert
|
||||
if (!confirm(i18n.t('repo.settings.actions.delete.confirm'))) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -41,16 +41,16 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { computed, inject, onMounted, Ref, ref, watch } from 'vue';
|
||||
import { computed, inject, onMounted, ref, watch, type Ref } from 'vue';
|
||||
|
||||
import { SelectOption } from '~/components/form/form.types';
|
||||
import type { SelectOption } from '~/components/form/form.types';
|
||||
import InputField from '~/components/form/InputField.vue';
|
||||
import SelectField from '~/components/form/SelectField.vue';
|
||||
import Settings from '~/components/layout/Settings.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import useConfig from '~/compositions/useConfig';
|
||||
import { usePaginate } from '~/compositions/usePaginate';
|
||||
import { Repo } from '~/lib/api/types';
|
||||
import type { Repo } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
>
|
||||
<span>{{ cron.name }}</span>
|
||||
<span v-if="cron.next_exec && cron.next_exec > 0" class="ml-auto">
|
||||
{{ $t('repo.settings.crons.next_exec') }}: {{ date.toLocaleString(new Date(cron.next_exec * 1000)) }}</span
|
||||
>
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
{{ $t('repo.settings.crons.next_exec') }}: {{ date.toLocaleString(new Date(cron.next_exec * 1000)) }}
|
||||
</span>
|
||||
<span v-else class="ml-auto">{{ $t('repo.settings.crons.not_executed_yet') }}</span>
|
||||
<IconButton icon="play" class="ml-auto w-8 h-8" :title="$t('repo.settings.crons.run')" @click="runCron(cron)" />
|
||||
<IconButton icon="edit" class="w-8 h-8" :title="$t('repo.settings.crons.edit')" @click="selectedCron = cron" />
|
||||
|
@ -69,6 +70,7 @@
|
|||
|
||||
<div v-if="isEditingCron" class="ml-auto mb-4">
|
||||
<span v-if="selectedCron.next_exec && selectedCron.next_exec > 0" class="text-wp-text-100">
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
{{ $t('repo.settings.crons.next_exec') }}:
|
||||
{{ date.toLocaleString(new Date(selectedCron.next_exec * 1000)) }}
|
||||
</span>
|
||||
|
@ -90,7 +92,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, ref } from 'vue';
|
||||
import { computed, inject, ref, type Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
|
@ -104,7 +106,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction';
|
|||
import { useDate } from '~/compositions/useDate';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Cron, Repo } from '~/lib/api/types';
|
||||
import type { Cron, Repo } from '~/lib/api/types';
|
||||
import router from '~/router';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
|
@ -141,7 +143,7 @@ const { doSubmit: createCron, isLoading: isSaving } = useAsyncAction(async () =>
|
|||
await apiClient.createCron(repo.value.id, selectedCron.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(isEditingCron.value ? 'repo.settings.crons.saved' : i18n.t('repo.settings.crons.created')),
|
||||
title: isEditingCron.value ? i18n.t('repo.settings.crons.saved') : i18n.t('repo.settings.crons.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedCron.value = undefined;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<template #description>
|
||||
<i18n-t keypath="repo.settings.general.pipeline_path.desc" tag="p" class="text-sm text-wp-text-alt-100">
|
||||
<span class="code-box-inline px-1">{{ $t('repo.settings.general.pipeline_path.desc_path_example') }}</span>
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
<span class="code-box-inline px-1">/</span>
|
||||
</i18n-t>
|
||||
</template>
|
||||
|
@ -97,13 +98,13 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, onMounted, Ref, ref } from 'vue';
|
||||
import { inject, onMounted, ref, type Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
import Checkbox from '~/components/form/Checkbox.vue';
|
||||
import CheckboxesField from '~/components/form/CheckboxesField.vue';
|
||||
import { CheckboxOption, RadioOption } from '~/components/form/form.types';
|
||||
import type { CheckboxOption, RadioOption } from '~/components/form/form.types';
|
||||
import InputField from '~/components/form/InputField.vue';
|
||||
import NumberField from '~/components/form/NumberField.vue';
|
||||
import RadioField from '~/components/form/RadioField.vue';
|
||||
|
@ -113,7 +114,7 @@ import useApiClient from '~/compositions/useApiClient';
|
|||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useAuthentication from '~/compositions/useAuthentication';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { Repo, RepoSettings, RepoVisibility, WebhookEvents } from '~/lib/api/types';
|
||||
import { RepoVisibility, WebhookEvents, type Repo, type RepoSettings } from '~/lib/api/types';
|
||||
import { useRepoStore } from '~/store/repos';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, ref } from 'vue';
|
||||
import { computed, inject, ref, type Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
|
@ -88,8 +88,7 @@ import useApiClient from '~/compositions/useApiClient';
|
|||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Repo } from '~/lib/api/types';
|
||||
import { Registry } from '~/lib/api/types/registry';
|
||||
import type { Registry, Repo } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
|
@ -124,9 +123,9 @@ const { doSubmit: createRegistry, isLoading: isSaving } = useAsyncAction(async (
|
|||
await apiClient.createRegistry(repo.value.id, selectedRegistry.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(
|
||||
isEditingRegistry.value ? 'repo.settings.registries.saved' : i18n.t('repo.settings.registries.created'),
|
||||
),
|
||||
title: isEditingRegistry.value
|
||||
? i18n.t('repo.settings.registries.saved')
|
||||
: i18n.t('repo.settings.registries.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedRegistry.value = undefined;
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { computed, inject, Ref, ref } from 'vue';
|
||||
import { computed, inject, ref, type Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
|
@ -36,7 +36,7 @@ import useApiClient from '~/compositions/useApiClient';
|
|||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Repo, Secret, WebhookEvents } from '~/lib/api/types';
|
||||
import { WebhookEvents, type Repo, type Secret } from '~/lib/api/types';
|
||||
|
||||
const emptySecret: Partial<Secret> = {
|
||||
name: '',
|
||||
|
@ -77,9 +77,7 @@ const { resetPage, data: _secrets } = usePagination(loadSecrets, () => !selected
|
|||
const secrets = computed(() => {
|
||||
const secretsList: Record<string, Secret & { edit?: boolean; level: 'repo' | 'org' | 'global' }> = {};
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const level of ['repo', 'org', 'global']) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const secret of _secrets.value) {
|
||||
if (
|
||||
((level === 'repo' && secret.repo_id !== 0 && secret.org_id === 0) ||
|
||||
|
@ -118,7 +116,7 @@ const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async ()
|
|||
await apiClient.createSecret(repo.value.id, selectedSecret.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(isEditingSecret.value ? 'secrets.saved' : 'secrets.created'),
|
||||
title: isEditingSecret.value ? i18n.t('secrets.saved') : i18n.t('secrets.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedSecret.value = undefined;
|
||||
|
|
|
@ -59,10 +59,10 @@ import { useI18n } from 'vue-i18n';
|
|||
|
||||
import Button from '~/components/atomic/Button.vue';
|
||||
import CheckboxesField from '~/components/form/CheckboxesField.vue';
|
||||
import { CheckboxOption } from '~/components/form/form.types';
|
||||
import type { CheckboxOption } from '~/components/form/form.types';
|
||||
import InputField from '~/components/form/InputField.vue';
|
||||
import TextField from '~/components/form/TextField.vue';
|
||||
import { Secret, WebhookEvents } from '~/lib/api/types';
|
||||
import { WebhookEvents, type Secret } from '~/lib/api/types';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Partial<Secret>;
|
||||
|
|
|
@ -42,7 +42,7 @@ import { useI18n } from 'vue-i18n';
|
|||
import Badge from '~/components/atomic/Badge.vue';
|
||||
import IconButton from '~/components/atomic/IconButton.vue';
|
||||
import ListItem from '~/components/atomic/ListItem.vue';
|
||||
import { Secret } from '~/lib/api/types';
|
||||
import type { Secret } from '~/lib/api/types';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: (Secret & { edit?: boolean })[];
|
||||
|
@ -64,8 +64,8 @@ function editSecret(secret: Secret) {
|
|||
|
||||
function deleteSecret(secret: Secret) {
|
||||
// TODO: use proper dialog
|
||||
// eslint-disable-next-line no-alert, no-restricted-globals
|
||||
if (!confirm(i18n.t('repo.settings.secrets.delete_confirm'))) {
|
||||
// eslint-disable-next-line no-alert
|
||||
if (!confirm(i18n.t('secrets.delete_confirm'))) {
|
||||
return;
|
||||
}
|
||||
emit('delete', secret);
|
||||
|
|
|
@ -23,8 +23,9 @@
|
|||
:href="`${address}/swagger/index.html`"
|
||||
target="_blank"
|
||||
class="ml-4 text-wp-link-100 hover:text-wp-link-200"
|
||||
>{{ $t('user.settings.cli_and_api.swagger_ui') }}</a
|
||||
>
|
||||
{{ $t('user.settings.cli_and_api.swagger_ui') }}
|
||||
</a>
|
||||
</template>
|
||||
<pre class="code-box">{{ usageWithCurl }}</pre>
|
||||
</InputField>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="ml-2">
|
||||
<h1 class="text-xl text-wp-text-100">{{ $t('secrets.secrets') }}</h1>
|
||||
<p class="text-sm text-wp-text-alt-100">
|
||||
{{ $t('secrets.desc') }}
|
||||
{{ $t('user.settings.secrets.desc') }}
|
||||
<DocsLink :topic="$t('secrets.secrets')" url="docs/usage/secrets" />
|
||||
</p>
|
||||
</div>
|
||||
|
@ -51,7 +51,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction';
|
|||
import useAuthentication from '~/compositions/useAuthentication';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Secret, WebhookEvents } from '~/lib/api/types';
|
||||
import { WebhookEvents, type Secret } from '~/lib/api/types';
|
||||
|
||||
const emptySecret: Partial<Secret> = {
|
||||
name: '',
|
||||
|
@ -92,7 +92,7 @@ const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async ()
|
|||
await apiClient.createOrgSecret(user.org_id, selectedSecret.value);
|
||||
}
|
||||
notifications.notify({
|
||||
title: i18n.t(isEditingSecret.value ? 'user.settings.secrets.saved' : 'user.settings.secrets.created'),
|
||||
title: isEditingSecret.value ? i18n.t('secrets.saved') : i18n.t('secrets.created'),
|
||||
type: 'success',
|
||||
});
|
||||
selectedSecret.value = undefined;
|
||||
|
@ -101,7 +101,7 @@ const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async ()
|
|||
|
||||
const { doSubmit: deleteSecret, isLoading: isDeleting } = useAsyncAction(async (_secret: Secret) => {
|
||||
await apiClient.deleteOrgSecret(user.org_id, _secret.name);
|
||||
notifications.notify({ title: i18n.t('user.settings.secrets.deleted'), type: 'success' });
|
||||
notifications.notify({ title: i18n.t('secrets.deleted'), type: 'success' });
|
||||
resetPage();
|
||||
});
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ export default (): WoodpeckerClient => {
|
|||
const config = useConfig();
|
||||
const server = config.rootPath;
|
||||
const token = null;
|
||||
const csrf = config.csrf || null;
|
||||
const csrf = config.csrf ?? null;
|
||||
|
||||
apiClient = new WoodpeckerClient(server, token, csrf);
|
||||
}
|
||||
|
|
|
@ -4,14 +4,7 @@ import useNotifications from '~/compositions/useNotifications';
|
|||
|
||||
const notifications = useNotifications();
|
||||
|
||||
export type UseSubmitOptions = {
|
||||
showErrorNotification: false;
|
||||
};
|
||||
|
||||
export function useAsyncAction<T extends unknown[]>(
|
||||
action: (...a: T) => void | Promise<void>,
|
||||
options?: UseSubmitOptions,
|
||||
) {
|
||||
export function useAsyncAction<T extends unknown[]>(action: (...a: T) => void | Promise<void>) {
|
||||
const isLoading = ref(false);
|
||||
|
||||
async function doSubmit(...a: T) {
|
||||
|
@ -23,9 +16,7 @@ export function useAsyncAction<T extends unknown[]>(
|
|||
try {
|
||||
await action(...a);
|
||||
} catch (error) {
|
||||
if (options?.showErrorNotification) {
|
||||
notifications.notify({ title: (error as Error).message, type: 'error' });
|
||||
}
|
||||
notifications.notify({ title: (error as Error).message, type: 'error' });
|
||||
}
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ export default () =>
|
|||
user: useConfig().user,
|
||||
|
||||
authenticate(url?: string) {
|
||||
if (url) {
|
||||
if (url !== undefined) {
|
||||
const config = useUserConfig();
|
||||
config.setUserConfig('redirectUrl', url);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { User } from '~/lib/api/types';
|
||||
import type { User } from '~/lib/api/types';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
@ -13,11 +13,11 @@ declare global {
|
|||
}
|
||||
|
||||
export default () => ({
|
||||
user: window.WOODPECKER_USER || null,
|
||||
user: window.WOODPECKER_USER ?? null,
|
||||
version: window.WOODPECKER_VERSION,
|
||||
skipVersionCheck: window.WOODPECKER_SKIP_VERSION_CHECK || false,
|
||||
csrf: window.WOODPECKER_CSRF || null,
|
||||
forge: window.WOODPECKER_FORGE || null,
|
||||
rootPath: window.WOODPECKER_ROOT_PATH || '',
|
||||
enableSwagger: window.WOODPECKER_ENABLE_SWAGGER || false,
|
||||
skipVersionCheck: window.WOODPECKER_SKIP_VERSION_CHECK === true || false,
|
||||
csrf: window.WOODPECKER_CSRF ?? null,
|
||||
forge: window.WOODPECKER_FORGE ?? null,
|
||||
rootPath: window.WOODPECKER_ROOT_PATH ?? '',
|
||||
enableSwagger: window.WOODPECKER_ENABLE_SWAGGER === true || false,
|
||||
});
|
||||
|
|
|
@ -29,7 +29,7 @@ export function useDate() {
|
|||
|
||||
async function setDayjsLocale(locale: string) {
|
||||
if (!addedLocales.includes(locale)) {
|
||||
const l = await import(`~/assets/dayjsLocales/${locale}.js`);
|
||||
const l = (await import(`~/assets/dayjsLocales/${locale}.js`)) as { default: string };
|
||||
dayjs.locale(l.default);
|
||||
} else {
|
||||
dayjs.locale(locale);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { computed, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch, type Ref } from 'vue';
|
||||
|
||||
export function useElapsedTime(running: Ref<boolean>, startTime: Ref<number | undefined>) {
|
||||
const time = ref<number | undefined>(startTime.value);
|
||||
|
|
|
@ -2,7 +2,7 @@ import { computed, ref, watch } from 'vue';
|
|||
|
||||
import useConfig from '~/compositions/useConfig';
|
||||
import { useTheme } from '~/compositions/useTheme';
|
||||
import { PipelineStatus } from '~/lib/api/types';
|
||||
import type { PipelineStatus } from '~/lib/api/types';
|
||||
|
||||
const { theme } = useTheme();
|
||||
const darkMode = computed(() => theme.value);
|
||||
|
|
|
@ -16,9 +16,9 @@ export const i18n = createI18n({
|
|||
});
|
||||
|
||||
const loadLocaleMessages = async (locale: string) => {
|
||||
const { default: messages } = await import(`~/assets/locales/${locale}.json`);
|
||||
const messages = (await import(`~/assets/locales/${locale}.json`)) as { default: any };
|
||||
|
||||
i18n.global.setLocaleMessage(locale, messages);
|
||||
i18n.global.setLocaleMessage(locale, messages.default);
|
||||
|
||||
return nextTick();
|
||||
};
|
||||
|
@ -31,6 +31,6 @@ export const setI18nLanguage = async (lang: string): Promise<void> => {
|
|||
await setDayjsLocale(lang);
|
||||
};
|
||||
|
||||
loadLocaleMessages(fallbackLocale);
|
||||
loadLocaleMessages(userLanguage);
|
||||
setDayjsLocale(userLanguage);
|
||||
loadLocaleMessages(fallbackLocale).catch(console.error);
|
||||
loadLocaleMessages(userLanguage).catch(console.error);
|
||||
setDayjsLocale(userLanguage).catch(console.error);
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { inject as vueInject, InjectionKey, provide as vueProvide, Ref } from 'vue';
|
||||
import type { InjectionKey, Ref } from 'vue';
|
||||
import { inject as vueInject, provide as vueProvide } from 'vue';
|
||||
|
||||
import { Org, OrgPermissions, Repo } from '~/lib/api/types';
|
||||
import type { Org, OrgPermissions, Repo } from '~/lib/api/types';
|
||||
|
||||
export type InjectKeys = {
|
||||
export interface InjectKeys {
|
||||
repo: Ref<Repo>;
|
||||
org: Ref<Org | undefined>;
|
||||
'org-permissions': Ref<OrgPermissions | undefined>;
|
||||
};
|
||||
}
|
||||
|
||||
export function inject<T extends keyof InjectKeys>(key: T): InjectKeys[T] {
|
||||
const value = vueInject<InjectKeys[T]>(key);
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import Notifications, { NotificationsOptions, notify } from '@kyvg/vue3-notification';
|
||||
import Notifications, { notify, type NotificationsOptions } from '@kyvg/vue3-notification';
|
||||
|
||||
export const notifications = Notifications;
|
||||
|
||||
function notifyError(err: unknown, args: NotificationsOptions | string = {}): void {
|
||||
// eslint-disable-next-line no-console
|
||||
function notifyError(err: Error, args: NotificationsOptions | string = {}): void {
|
||||
console.error(err);
|
||||
|
||||
const mArgs = typeof args === 'string' ? { title: args } : args;
|
||||
const title = mArgs?.title || (err as Error)?.message || `${err}`;
|
||||
const title = mArgs?.title ?? err?.message ?? err?.toString();
|
||||
|
||||
notify({ type: 'error', ...mArgs, title });
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { type Ref, watch } from 'vue';
|
||||
import { watch, type Ref } from 'vue';
|
||||
|
||||
import { usePagination } from './usePaginate';
|
||||
|
||||
|
@ -18,11 +18,11 @@ async function waitForState<T>(ref: Ref<T>, expected: T): Promise<void> {
|
|||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
// TODO enable again with eslint-plugin-promise eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
export const mountComposition = (cb: () => void) => {
|
||||
const wrapper = shallowMount({
|
||||
setup() {
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
// TODO enable again with eslint-plugin-promise eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
cb();
|
||||
return {};
|
||||
},
|
||||
|
@ -119,7 +119,7 @@ describe('usePaginate', () => {
|
|||
usePaginationComposition.nextPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
usePaginationComposition.resetPage();
|
||||
void usePaginationComposition.resetPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
expect(usePaginationComposition.data.value.length).toBe(3);
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { useInfiniteScroll } from '@vueuse/core';
|
||||
import { onMounted, Ref, ref, UnwrapRef, watch } from 'vue';
|
||||
import { onMounted, ref, watch, type Ref, type UnwrapRef } from 'vue';
|
||||
|
||||
export async function usePaginate<T>(getSingle: (page: number) => Promise<T[]>): Promise<T[]> {
|
||||
let hasMore = true;
|
||||
let page = 1;
|
||||
const result: T[] = [];
|
||||
while (hasMore) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const singleRes = await getSingle(page);
|
||||
result.push(...singleRes);
|
||||
hasMore = singleRes.length !== 0;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { computed, Ref } from 'vue';
|
||||
import { computed, type Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useDate } from '~/compositions/useDate';
|
||||
import { useElapsedTime } from '~/compositions/useElapsedTime';
|
||||
import { Pipeline } from '~/lib/api/types';
|
||||
import type { Pipeline } from '~/lib/api/types';
|
||||
import { convertEmojis } from '~/utils/emoji';
|
||||
|
||||
const { toLocaleString, timeAgo, prettyDuration } = useDate();
|
||||
|
|
|
@ -5,20 +5,20 @@ import { usePipelineStore } from '~/store/pipelines';
|
|||
|
||||
import useAuthentication from './useAuthentication';
|
||||
|
||||
const { userConfig, setUserConfig } = useUserConfig();
|
||||
const userConfig = useUserConfig();
|
||||
|
||||
export default () => {
|
||||
const pipelineStore = usePipelineStore();
|
||||
const { isAuthenticated } = useAuthentication();
|
||||
|
||||
const isOpen = computed(() => userConfig.value.isPipelineFeedOpen && !!isAuthenticated);
|
||||
const isOpen = computed(() => userConfig.userConfig.value.isPipelineFeedOpen && !!isAuthenticated);
|
||||
|
||||
function toggle() {
|
||||
setUserConfig('isPipelineFeedOpen', !userConfig.value.isPipelineFeedOpen);
|
||||
userConfig.setUserConfig('isPipelineFeedOpen', !userConfig.userConfig.value.isPipelineFeedOpen);
|
||||
}
|
||||
|
||||
function close() {
|
||||
setUserConfig('isPipelineFeedOpen', false);
|
||||
userConfig.setUserConfig('isPipelineFeedOpen', false);
|
||||
}
|
||||
|
||||
const sortedPipelines = toRef(pipelineStore, 'pipelineFeed');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Fuse from 'fuse.js';
|
||||
import { computed, Ref } from 'vue';
|
||||
import { computed, type Ref } from 'vue';
|
||||
|
||||
import { Repo } from '~/lib/api/types';
|
||||
import type { Repo } from '~/lib/api/types';
|
||||
|
||||
/*
|
||||
* Compares Repos lexicographically using owner/name .
|
||||
|
@ -9,7 +9,7 @@ import { Repo } from '~/lib/api/types';
|
|||
function repoCompare(a: Repo, b: Repo) {
|
||||
const x = `${a.owner}/${a.name}`;
|
||||
const y = `${b.owner}/${b.name}`;
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
|
||||
return x === y ? 0 : x > y ? 1 : -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { RouteLocationRaw, useRouter } from 'vue-router';
|
||||
import { useRouter, type RouteLocationRaw } from 'vue-router';
|
||||
|
||||
export function useRouteBack(to: RouteLocationRaw) {
|
||||
const router = useRouter();
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { inject, onMounted, provide, Ref, ref } from 'vue';
|
||||
import { inject, onMounted, provide, ref, type Ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
export type Tab = {
|
||||
export interface Tab {
|
||||
id: string;
|
||||
title: string;
|
||||
icon?: string;
|
||||
iconClass?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function useTabsProvider({
|
||||
activeTab,
|
||||
|
@ -29,7 +29,7 @@ export function useTabsProvider({
|
|||
}
|
||||
|
||||
const hashTab = route.hash.replace(/^#/, '');
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
||||
activeTab.value = hashTab || tabs.value[0].id;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ import { computed, ref } from 'vue';
|
|||
|
||||
const USER_CONFIG_KEY = 'woodpecker-user-config';
|
||||
|
||||
type UserConfig = {
|
||||
interface UserConfig {
|
||||
isPipelineFeedOpen: boolean;
|
||||
redirectUrl: string;
|
||||
};
|
||||
}
|
||||
|
||||
const defaultUserConfig: UserConfig = {
|
||||
isPipelineFeedOpen: false,
|
||||
|
@ -14,11 +14,11 @@ const defaultUserConfig: UserConfig = {
|
|||
|
||||
function loadUserConfig(): UserConfig {
|
||||
const lsData = localStorage.getItem(USER_CONFIG_KEY);
|
||||
if (!lsData) {
|
||||
if (lsData === null) {
|
||||
return defaultUserConfig;
|
||||
}
|
||||
|
||||
return JSON.parse(lsData);
|
||||
return JSON.parse(lsData) as UserConfig;
|
||||
}
|
||||
|
||||
const config = ref<UserConfig>(loadUserConfig());
|
||||
|
|
|
@ -5,11 +5,11 @@ import { onMounted, ref } from 'vue';
|
|||
import useAuthentication from './useAuthentication';
|
||||
import useConfig from './useConfig';
|
||||
|
||||
type VersionInfo = {
|
||||
interface VersionInfo {
|
||||
latest: string;
|
||||
rc: string;
|
||||
next: string;
|
||||
};
|
||||
}
|
||||
|
||||
const version = ref<{
|
||||
latest: string | undefined;
|
||||
|
@ -22,10 +22,9 @@ const version = ref<{
|
|||
async function fetchVersion(): Promise<VersionInfo | undefined> {
|
||||
try {
|
||||
const resp = await fetch('https://woodpecker-ci.org/version.json');
|
||||
const json = await resp.json();
|
||||
const json = (await resp.json()) as VersionInfo;
|
||||
return json;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to fetch version info', error);
|
||||
return undefined;
|
||||
}
|
||||
|
@ -45,7 +44,7 @@ export function useVersion() {
|
|||
const usesNext = current.startsWith('next');
|
||||
|
||||
const { user } = useAuthentication();
|
||||
if (config.skipVersionCheck || !user?.admin) {
|
||||
if (config.skipVersionCheck || user?.admin !== true) {
|
||||
version.value = {
|
||||
latest: undefined,
|
||||
current,
|
||||
|
|
|
@ -1,27 +1,28 @@
|
|||
export type ApiError = {
|
||||
export interface ApiError {
|
||||
status: number;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function encodeQueryString(_params: Record<string, string | number | boolean | undefined> = {}): string {
|
||||
const params: Record<string, string | number | boolean> = {};
|
||||
type QueryParams = Record<string, string | number | boolean>;
|
||||
|
||||
Object.keys(_params).forEach((key) => {
|
||||
const val = _params[key];
|
||||
export function encodeQueryString(_params: unknown = {}): string {
|
||||
const __params = _params as QueryParams;
|
||||
const params: QueryParams = {};
|
||||
|
||||
Object.keys(__params).forEach((key) => {
|
||||
const val = __params[key];
|
||||
if (val !== undefined) {
|
||||
params[key] = val;
|
||||
}
|
||||
});
|
||||
|
||||
return params
|
||||
? Object.keys(params)
|
||||
.sort()
|
||||
.map((key) => {
|
||||
const val = params[key];
|
||||
return `${encodeURIComponent(key)}=${encodeURIComponent(val)}`;
|
||||
})
|
||||
.join('&')
|
||||
: '';
|
||||
return Object.keys(params)
|
||||
.sort()
|
||||
.map((key) => {
|
||||
const val = params[key];
|
||||
return `${encodeURIComponent(key)}=${encodeURIComponent(val)}`;
|
||||
})
|
||||
.join('&');
|
||||
}
|
||||
|
||||
export default class ApiClient {
|
||||
|
@ -43,11 +44,11 @@ export default class ApiClient {
|
|||
const res = await fetch(`${this.server}${path}`, {
|
||||
method,
|
||||
headers: {
|
||||
...(method !== 'GET' && this.csrf ? { 'X-CSRF-TOKEN': this.csrf } : {}),
|
||||
...(this.token ? { Authorization: `Bearer ${this.token}` } : {}),
|
||||
...(data ? { 'Content-Type': 'application/json' } : {}),
|
||||
...(method !== 'GET' && this.csrf !== null ? { 'X-CSRF-TOKEN': this.csrf } : {}),
|
||||
...(this.token !== null ? { Authorization: `Bearer ${this.token}` } : {}),
|
||||
...(data !== undefined ? { 'Content-Type': 'application/json' } : {}),
|
||||
},
|
||||
body: data ? JSON.stringify(data) : undefined,
|
||||
body: data !== undefined ? JSON.stringify(data) : undefined,
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
|
@ -62,7 +63,7 @@ export default class ApiClient {
|
|||
}
|
||||
|
||||
const contentType = res.headers.get('Content-Type');
|
||||
if (contentType && contentType.startsWith('application/json')) {
|
||||
if (contentType !== null && contentType.startsWith('application/json')) {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
|
@ -87,15 +88,15 @@ export default class ApiClient {
|
|||
|
||||
_subscribe<T>(path: string, callback: (data: T) => void, opts = { reconnect: true }) {
|
||||
const query = encodeQueryString({
|
||||
access_token: this.token || undefined,
|
||||
access_token: this.token ?? undefined,
|
||||
});
|
||||
let _path = this.server ? this.server + path : path;
|
||||
_path = this.token ? `${_path}?${query}` : _path;
|
||||
_path = this.token !== null ? `${_path}?${query}` : _path;
|
||||
|
||||
const events = new EventSource(_path);
|
||||
events.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data) as T;
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
const data = JSON.parse(event.data as string) as T;
|
||||
// TODO enable again with eslint-plugin-promise eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
callback(data);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import ApiClient, { encodeQueryString } from './client';
|
||||
import {
|
||||
import type {
|
||||
Agent,
|
||||
Cron,
|
||||
Org,
|
||||
|
@ -18,27 +18,27 @@ import {
|
|||
User,
|
||||
} from './types';
|
||||
|
||||
type RepoListOptions = {
|
||||
interface RepoListOptions {
|
||||
all?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// PipelineOptions is the data for creating a new pipeline
|
||||
type PipelineOptions = {
|
||||
interface PipelineOptions {
|
||||
branch: string;
|
||||
variables: Record<string, string>;
|
||||
};
|
||||
}
|
||||
|
||||
type DeploymentOptions = {
|
||||
interface DeploymentOptions {
|
||||
id: string;
|
||||
environment: string;
|
||||
task: string;
|
||||
variables: Record<string, string>;
|
||||
};
|
||||
}
|
||||
|
||||
type PaginationOptions = {
|
||||
interface PaginationOptions {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export default class WoodpeckerClient extends ApiClient {
|
||||
getRepoList(opts?: RepoListOptions): Promise<Repo[]> {
|
||||
|
@ -336,10 +336,10 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
}
|
||||
|
||||
repairAllRepos(): Promise<unknown> {
|
||||
return this._post(`/api/repos/repair`) as Promise<unknown>;
|
||||
return this._post(`/api/repos/repair`);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
// TODO enable again with eslint-plugin-promise eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
on(callback: (data: { pipeline?: Pipeline; repo?: Repo }) => void): EventSource {
|
||||
return this._subscribe('/api/stream/events', callback, {
|
||||
reconnect: true,
|
||||
|
@ -350,7 +350,7 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
repoId: number,
|
||||
pipeline: number,
|
||||
step: number,
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
// TODO enable again with eslint-plugin-promise eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
callback: (data: PipelineLog) => void,
|
||||
): EventSource {
|
||||
return this._subscribe(`/api/stream/logs/${repoId}/${pipeline}/${step}`, callback, {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export type Agent = {
|
||||
export interface Agent {
|
||||
id: number;
|
||||
name: string;
|
||||
token: string;
|
||||
|
@ -10,4 +10,4 @@ export type Agent = {
|
|||
capacity: number;
|
||||
version: string;
|
||||
no_schedule: boolean;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export type Cron = {
|
||||
export interface Cron {
|
||||
id: number;
|
||||
name: string;
|
||||
branch: string;
|
||||
schedule: string;
|
||||
next_exec: number;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// A version control organization.
|
||||
export type Org = {
|
||||
export interface Org {
|
||||
// The name of the organization.
|
||||
id: number;
|
||||
name: string;
|
||||
is_user: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export type OrgPermissions = {
|
||||
export interface OrgPermissions {
|
||||
member: boolean;
|
||||
admin: boolean;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { WebhookEvents } from './webhook';
|
||||
import type { WebhookEvents } from './webhook';
|
||||
|
||||
export type PipelineError<D = unknown> = {
|
||||
export interface PipelineError<D = unknown> {
|
||||
type: string;
|
||||
message: string;
|
||||
data?: D;
|
||||
is_warning: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// A pipeline for a repository.
|
||||
export type Pipeline = {
|
||||
export interface Pipeline {
|
||||
id: number;
|
||||
|
||||
// The pipeline number.
|
||||
|
@ -89,7 +89,7 @@ export type Pipeline = {
|
|||
workflows?: PipelineWorkflow[];
|
||||
|
||||
changed_files?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export type PipelineStatus =
|
||||
| 'blocked'
|
||||
|
@ -103,7 +103,7 @@ export type PipelineStatus =
|
|||
| 'started'
|
||||
| 'success';
|
||||
|
||||
export type PipelineWorkflow = {
|
||||
export interface PipelineWorkflow {
|
||||
id: number;
|
||||
pipeline_id: number;
|
||||
pid: number;
|
||||
|
@ -115,9 +115,9 @@ export type PipelineWorkflow = {
|
|||
agent_id?: number;
|
||||
error?: string;
|
||||
children: PipelineStep[];
|
||||
};
|
||||
}
|
||||
|
||||
export type PipelineStep = {
|
||||
export interface PipelineStep {
|
||||
id: number;
|
||||
uuid: string;
|
||||
pipeline_id: number;
|
||||
|
@ -130,21 +130,22 @@ export type PipelineStep = {
|
|||
end_time?: number;
|
||||
error?: string;
|
||||
type?: StepType;
|
||||
};
|
||||
}
|
||||
|
||||
export type PipelineLog = {
|
||||
export interface PipelineLog {
|
||||
id: number;
|
||||
step_id: number;
|
||||
time: number;
|
||||
line: number;
|
||||
data: string; // base64 encoded
|
||||
type: number;
|
||||
};
|
||||
}
|
||||
|
||||
export type PipelineFeed = Pipeline & {
|
||||
repo_id: number;
|
||||
};
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
export enum StepType {
|
||||
Clone = 'clone',
|
||||
Service = 'service',
|
||||
|
@ -152,3 +153,4 @@ export enum StepType {
|
|||
Commands = 'commands',
|
||||
Cache = 'cache',
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// A config for a pipeline.
|
||||
export type PipelineConfig = {
|
||||
export interface PipelineConfig {
|
||||
hash: string;
|
||||
name: string;
|
||||
data: string;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// A version control pull request.
|
||||
export type PullRequest = {
|
||||
export interface PullRequest {
|
||||
// The index of the pull request.
|
||||
index: string;
|
||||
// The title of the pull request.
|
||||
title: string;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export type Task = {
|
||||
export interface Task {
|
||||
id: number;
|
||||
data: string;
|
||||
labels: { [key: string]: string };
|
||||
|
@ -6,20 +6,20 @@ export type Task = {
|
|||
dep_status: { [key: string]: string };
|
||||
run_on: string[];
|
||||
agent_id: number;
|
||||
};
|
||||
}
|
||||
|
||||
export type QueueStats = {
|
||||
export interface QueueStats {
|
||||
worker_count: number;
|
||||
pending_count: number;
|
||||
waiting_on_deps_count: number;
|
||||
running_count: number;
|
||||
completed_count: number;
|
||||
};
|
||||
}
|
||||
|
||||
export type QueueInfo = {
|
||||
export interface QueueInfo {
|
||||
pending: Task[];
|
||||
waiting_on_deps: Task[];
|
||||
running: Task[];
|
||||
stats: QueueStats;
|
||||
paused: boolean;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export type Registry = {
|
||||
export interface Registry {
|
||||
id: string;
|
||||
address: string;
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// A version control repository.
|
||||
export type Repo = {
|
||||
export interface Repo {
|
||||
// Is the repo currently active or not
|
||||
active: boolean;
|
||||
|
||||
|
@ -70,13 +70,15 @@ export type Repo = {
|
|||
cancel_previous_pipeline_events: string[];
|
||||
|
||||
netrc_only_trusted: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
export enum RepoVisibility {
|
||||
Public = 'public',
|
||||
Private = 'private',
|
||||
Internal = 'internal',
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
export type RepoSettings = Pick<
|
||||
Repo,
|
||||
|
@ -91,9 +93,9 @@ export type RepoSettings = Pick<
|
|||
| 'netrc_only_trusted'
|
||||
>;
|
||||
|
||||
export type RepoPermissions = {
|
||||
export interface RepoPermissions {
|
||||
pull: boolean;
|
||||
push: boolean;
|
||||
admin: boolean;
|
||||
synced: number;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { WebhookEvents } from './webhook';
|
||||
import type { WebhookEvents } from './webhook';
|
||||
|
||||
export type Secret = {
|
||||
export interface Secret {
|
||||
id: string;
|
||||
repo_id: number;
|
||||
org_id: number;
|
||||
|
@ -8,4 +8,4 @@ export type Secret = {
|
|||
value: string;
|
||||
events: WebhookEvents[];
|
||||
images: string[];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// The user account.
|
||||
export type User = {
|
||||
export interface User {
|
||||
id: number;
|
||||
// The unique identifier for the account.
|
||||
|
||||
|
@ -20,4 +20,4 @@ export type User = {
|
|||
|
||||
org_id: number;
|
||||
// The ID of the org assigned to the user.
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
export enum WebhookEvents {
|
||||
Push = 'push',
|
||||
Tag = 'tag',
|
||||
|
@ -8,3 +9,4 @@ export enum WebhookEvents {
|
|||
Cron = 'cron',
|
||||
Manual = 'manual',
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -11,6 +11,7 @@ import { i18n } from '~/compositions/useI18n';
|
|||
import { notifications } from '~/compositions/useNotifications';
|
||||
import router from '~/router';
|
||||
|
||||
// eslint-disable-next-line ts/no-unsafe-argument
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(router);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component } from 'vue';
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
|
||||
import type { Component } from 'vue';
|
||||
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import useAuthentication from '~/compositions/useAuthentication';
|
||||
import useConfig from '~/compositions/useConfig';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { computed, reactive, Ref, ref } from 'vue';
|
||||
import { computed, reactive, ref, type Ref } from 'vue';
|
||||
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { Pipeline, PipelineFeed, PipelineWorkflow } from '~/lib/api/types';
|
||||
import type { Pipeline, PipelineFeed, PipelineWorkflow } from '~/lib/api/types';
|
||||
import { useRepoStore } from '~/store/repos';
|
||||
import { comparePipelines, comparePipelinesWithStatus, isPipelineActive } from '~/utils/helpers';
|
||||
|
||||
|
@ -13,7 +13,7 @@ export const usePipelineStore = defineStore('pipelines', () => {
|
|||
const pipelines: Map<number, Map<number, Pipeline>> = reactive(new Map());
|
||||
|
||||
function setPipeline(repoId: number, pipeline: Pipeline) {
|
||||
const repoPipelines = pipelines.get(repoId) || new Map();
|
||||
const repoPipelines = pipelines.get(repoId) || new Map<number, Pipeline>();
|
||||
repoPipelines.set(pipeline.number, {
|
||||
...(repoPipelines.get(pipeline.number) || {}),
|
||||
...pipeline,
|
||||
|
@ -27,7 +27,7 @@ export const usePipelineStore = defineStore('pipelines', () => {
|
|||
|
||||
function getPipeline(repoId: Ref<number>, _pipelineNumber: Ref<string>) {
|
||||
return computed(() => {
|
||||
const pipelineNumber = parseInt(_pipelineNumber.value, 10);
|
||||
const pipelineNumber = Number.parseInt(_pipelineNumber.value, 10);
|
||||
return pipelines.get(repoId.value)?.get(pipelineNumber);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { computed, reactive, Ref, ref } from 'vue';
|
||||
import { computed, reactive, ref, type Ref } from 'vue';
|
||||
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { Repo } from '~/lib/api/types';
|
||||
import type { Repo } from '~/lib/api/types';
|
||||
|
||||
export const useRepoStore = defineStore('repos', () => {
|
||||
const apiClient = useApiClient();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Pipeline, PipelineStep, PipelineWorkflow, Repo } from '~/lib/api/types';
|
||||
import type { Pipeline, PipelineStep, PipelineWorkflow, Repo } from '~/lib/api/types';
|
||||
|
||||
export function findStep(workflows: PipelineWorkflow[], pid: number): PipelineStep | undefined {
|
||||
return workflows.reduce(
|
||||
|
@ -24,20 +24,16 @@ export function findStep(workflows: PipelineWorkflow[], pid: number): PipelineSt
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if the process is in a completed state.
|
||||
*
|
||||
* @param {Object} step - The process object.
|
||||
* @returns {boolean}
|
||||
* @param {object} step - The process object.
|
||||
* @returns {boolean} true if the process is in a completed state
|
||||
*/
|
||||
export function isStepFinished(step: PipelineStep): boolean {
|
||||
return step.state !== 'running' && step.state !== 'pending';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the process is running.
|
||||
*
|
||||
* @param {Object} step - The process object.
|
||||
* @returns {boolean}
|
||||
* @param {object} step - The process object.
|
||||
* @returns {boolean} true if the process is running
|
||||
*/
|
||||
export function isStepRunning(step: PipelineStep): boolean {
|
||||
return step.state === 'running';
|
||||
|
@ -45,9 +41,9 @@ export function isStepRunning(step: PipelineStep): boolean {
|
|||
|
||||
/**
|
||||
* Compare two pipelines by creation timestamp.
|
||||
* @param {Object} a - A pipeline.
|
||||
* @param {Object} b - A pipeline.
|
||||
* @returns {number}
|
||||
* @param {object} a - A pipeline.
|
||||
* @param {object} b - A pipeline.
|
||||
* @returns {number} 0 if created at the same time, < 0 if b was create before a, > 0 otherwise
|
||||
*/
|
||||
export function comparePipelines(a: Pipeline, b: Pipeline): number {
|
||||
return (b.created_at || -1) - (a.created_at || -1);
|
||||
|
@ -56,9 +52,9 @@ export function comparePipelines(a: Pipeline, b: Pipeline): number {
|
|||
/**
|
||||
* Compare two pipelines by the status.
|
||||
* Giving pending, running, or started higher priority than other status
|
||||
* @param {Object} a - A pipeline.
|
||||
* @param {Object} b - A pipeline.
|
||||
* @returns {number}
|
||||
* @param {object} a - A pipeline.
|
||||
* @param {object} b - A pipeline.
|
||||
* @returns {number} 0 if status same priority, < 0 if b has higher priority, > 0 otherwise
|
||||
*/
|
||||
export function comparePipelinesWithStatus(a: Pipeline, b: Pipeline): number {
|
||||
const bPriority = ['pending', 'running', 'started'].includes(b.status) ? 1 : 0;
|
||||
|
@ -70,11 +66,9 @@ export function isPipelineActive(pipeline: Pipeline): boolean {
|
|||
return ['pending', 'running', 'started'].includes(pipeline.status);
|
||||
}
|
||||
|
||||
export function repoSlug(ownerOrRepo: Repo): string;
|
||||
export function repoSlug(ownerOrRepo: string, name: string): string;
|
||||
export function repoSlug(ownerOrRepo: string | Repo, name?: string): string {
|
||||
if (typeof ownerOrRepo === 'string') {
|
||||
if (!name) {
|
||||
if (name === undefined) {
|
||||
throw new Error('Please provide a name as well');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
<template>
|
||||
<div class="flex flex-col h-full w-full items-center justify-center">
|
||||
<p class="text-2xl mb-8">{{ $t('not_found.not_found') }}</p>
|
||||
<span
|
||||
><router-link class="text-blue-400" replace :to="{ name: 'home' }">{{
|
||||
$t('not_found.back_home')
|
||||
}}</router-link></span
|
||||
>
|
||||
<router-link class="text-blue-400" replace :to="{ name: 'home' }">
|
||||
{{ $t('not_found.back_home') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -45,7 +45,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction';
|
|||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { useRepoSearch } from '~/compositions/useRepoSearch';
|
||||
import { useRouteBack } from '~/compositions/useRouteBack';
|
||||
import { Repo } from '~/lib/api/types';
|
||||
import type { Repo } from '~/lib/api/types';
|
||||
|
||||
const router = useRouter();
|
||||
const apiClient = useApiClient();
|
||||
|
|
|
@ -8,13 +8,12 @@ import { useRoute, useRouter } from 'vue-router';
|
|||
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<{
|
||||
orgName: string;
|
||||
}>();
|
||||
const apiClient = useApiClient();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
onMounted(async () => {
|
||||
const org = await apiClient.lookupOrg(props.orgName);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<span>
|
||||
<router-link :to="{ name: 'org' }" class="hover:underline">
|
||||
{{ org.name }}
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
</router-link>
|
||||
/
|
||||
{{ $t('settings') }}
|
||||
|
|
|
@ -25,13 +25,13 @@ import IconButton from '~/components/atomic/IconButton.vue';
|
|||
import Scaffold from '~/components/layout/scaffold/Scaffold.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { provide } from '~/compositions/useInjectProvide';
|
||||
import { Org, OrgPermissions } from '~/lib/api/types';
|
||||
import type { Org, OrgPermissions } from '~/lib/api/types';
|
||||
|
||||
const props = defineProps<{
|
||||
orgId: string;
|
||||
}>();
|
||||
|
||||
const orgId = computed(() => parseInt(props.orgId, 10));
|
||||
const orgId = computed(() => Number.parseInt(props.orgId, 10));
|
||||
const apiClient = useApiClient();
|
||||
|
||||
const org = ref<Org>();
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, toRef } from 'vue';
|
||||
import { computed, inject, toRef, type Ref } from 'vue';
|
||||
|
||||
import PipelineList from '~/components/repo/pipeline/PipelineList.vue';
|
||||
import { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
import type { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
|
||||
const props = defineProps<{
|
||||
branch: string;
|
||||
|
|
|
@ -21,13 +21,13 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, watch } from 'vue';
|
||||
import { computed, inject, watch, type Ref } from 'vue';
|
||||
|
||||
import Badge from '~/components/atomic/Badge.vue';
|
||||
import ListItem from '~/components/atomic/ListItem.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { Repo } from '~/lib/api/types';
|
||||
import type { Repo } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
|
||||
|
|
|
@ -8,14 +8,13 @@ import { useRoute, useRouter } from 'vue-router';
|
|||
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<{
|
||||
repoOwner: string;
|
||||
repoName: string;
|
||||
}>();
|
||||
const apiClient = useApiClient();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
onMounted(async () => {
|
||||
const repo = await apiClient.lookupRepo(props.repoOwner, props.repoName);
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, Ref } from 'vue';
|
||||
import { inject, type Ref } from 'vue';
|
||||
|
||||
import PipelineList from '~/components/repo/pipeline/PipelineList.vue';
|
||||
import { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
import type { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
|
||||
const repo = inject<Ref<Repo>>('repo');
|
||||
const repoPermissions = inject<Ref<RepoPermissions>>('repo-permissions');
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, toRef } from 'vue';
|
||||
import { computed, inject, toRef, type Ref } from 'vue';
|
||||
|
||||
import PipelineList from '~/components/repo/pipeline/PipelineList.vue';
|
||||
import { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
import type { Pipeline, Repo, RepoPermissions } from '~/lib/api/types';
|
||||
|
||||
const props = defineProps<{
|
||||
pullRequest: string;
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
class="text-wp-text-100"
|
||||
:to="{ name: 'repo-pull-request', params: { pullRequest: pullRequest.index } }"
|
||||
>
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
<span class="text-wp-text-alt-100 <md:hidden">#{{ pullRequest.index }}</span>
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
<span class="text-wp-text-alt-100 <md:hidden mx-2">-</span>
|
||||
<span class="text-wp-text-100 <md:underline whitespace-nowrap overflow-hidden overflow-ellipsis">{{
|
||||
pullRequest.title
|
||||
|
@ -24,14 +26,14 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, Ref, watch } from 'vue';
|
||||
import { inject, watch, type Ref } from 'vue';
|
||||
|
||||
import Icon from '~/components/atomic/Icon.vue';
|
||||
import ListItem from '~/components/atomic/ListItem.vue';
|
||||
import Panel from '~/components/layout/Panel.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import { PullRequest, Repo } from '~/lib/api/types';
|
||||
import type { PullRequest, Repo } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
<span>
|
||||
<router-link :to="{ name: 'org', params: { orgId: repo!.org_id } }" class="hover:underline">
|
||||
{{ repo!.owner }}
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
</router-link>
|
||||
/
|
||||
<router-link :to="{ name: 'repo' }" class="hover:underline">
|
||||
{{ repo!.name }}
|
||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||
</router-link>
|
||||
/
|
||||
{{ $t('settings') }}
|
||||
|
@ -36,7 +38,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, onMounted, Ref } from 'vue';
|
||||
import { inject, onMounted, type Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
|
@ -50,7 +52,7 @@ import RegistriesTab from '~/components/repo/settings/RegistriesTab.vue';
|
|||
import SecretsTab from '~/components/repo/settings/SecretsTab.vue';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { useRouteBack } from '~/compositions/useRouteBack';
|
||||
import { Repo, RepoPermissions } from '~/lib/api/types';
|
||||
import type { Repo, RepoPermissions } from '~/lib/api/types';
|
||||
|
||||
const notifications = useNotifications();
|
||||
const router = useRouter();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue