mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-20 00:41: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": [
|
"words": [
|
||||||
"abool",
|
"abool",
|
||||||
"anbraten",
|
"anbraten",
|
||||||
|
"antfu",
|
||||||
"apimachinery",
|
"apimachinery",
|
||||||
"autoincr",
|
"autoincr",
|
||||||
"autoscaler",
|
"autoscaler",
|
||||||
|
@ -67,6 +68,7 @@
|
||||||
"httpsig",
|
"httpsig",
|
||||||
"HTTPURL",
|
"HTTPURL",
|
||||||
"httputil",
|
"httputil",
|
||||||
|
"ianvs",
|
||||||
"iconify",
|
"iconify",
|
||||||
"Infof",
|
"Infof",
|
||||||
"Informatyka",
|
"Informatyka",
|
||||||
|
@ -144,6 +146,7 @@
|
||||||
"tmpfs",
|
"tmpfs",
|
||||||
"tmpl",
|
"tmpl",
|
||||||
"tolerations",
|
"tolerations",
|
||||||
|
"tseslint",
|
||||||
"ttlcache",
|
"ttlcache",
|
||||||
"typecheck",
|
"typecheck",
|
||||||
"Typeflag",
|
"Typeflag",
|
||||||
|
|
10
.vscode/settings.json
vendored
10
.vscode/settings.json
vendored
|
@ -9,5 +9,13 @@
|
||||||
"go.lintTool": "golangci-lint",
|
"go.lintTool": "golangci-lint",
|
||||||
"go.lintFlags": ["--fast"],
|
"go.lintFlags": ["--fast"],
|
||||||
"eslint.workingDirectories": ["./web"],
|
"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",
|
"author": "Woodpecker CI",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
},
|
},
|
||||||
|
@ -10,7 +11,7 @@
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
"build": "vite build --base=/BASE_PATH",
|
"build": "vite build --base=/BASE_PATH",
|
||||||
"serve": "vite preview",
|
"serve": "vite preview",
|
||||||
"lint": "eslint --max-warnings 0 --ext .js,.ts,.vue,.json .",
|
"lint": "eslint --max-warnings 0 .",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier -c .",
|
"format:check": "prettier -c .",
|
||||||
"typecheck": "vue-tsc --noEmit",
|
"typecheck": "vue-tsc --noEmit",
|
||||||
|
@ -18,57 +19,54 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||||
"@kyvg/vue3-notification": "^3.1.3",
|
"@kyvg/vue3-notification": "^3.2.1",
|
||||||
"@vueuse/core": "^10.7.2",
|
"@vueuse/core": "^10.10.0",
|
||||||
"ansi_up": "^6.0.2",
|
"ansi_up": "^6.0.2",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.11",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"js-base64": "^3.7.6",
|
"js-base64": "^3.7.7",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"node-emoji": "^2.1.3",
|
"node-emoji": "^2.1.3",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
"semver": "^7.5.4",
|
"semver": "^7.6.2",
|
||||||
"vue": "^3.4.15",
|
"vue": "^3.4.27",
|
||||||
"vue-i18n": "^9.9.0",
|
"vue-i18n": "^9.13.1",
|
||||||
"vue-router": "^4.2.5"
|
"vue-router": "^4.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify/json": "^2.2.171",
|
"@antfu/eslint-config": "^2.20.0",
|
||||||
"@types/lodash": "^4.14.202",
|
"@eslint/js": "^9.4.0",
|
||||||
"@types/node": "^20.11.5",
|
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
|
||||||
"@types/node-emoji": "^2.0.0",
|
"@iconify/json": "^2.2.216",
|
||||||
"@types/prismjs": "^1.26.3",
|
"@intlify/eslint-plugin-vue-i18n": "3.0.0-next.13",
|
||||||
"@types/semver": "^7.5.6",
|
"@types/eslint__js": "^8.42.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
"@types/lodash": "^4.17.4",
|
||||||
"@typescript-eslint/parser": "^7.0.0",
|
"@types/node": "^20.14.2",
|
||||||
"@vitejs/plugin-vue": "^5.0.3",
|
"@types/node-emoji": "^2.1.0",
|
||||||
"@vue/compiler-sfc": "^3.4.15",
|
"@types/prismjs": "^1.26.4",
|
||||||
"@vue/test-utils": "^2.4.5",
|
"@types/semver": "^7.5.8",
|
||||||
"eslint": "^8.56.0",
|
"@types/tinycolor2": "^1.4.6",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"@vitejs/plugin-vue": "^5.0.5",
|
||||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
"@vue/compiler-sfc": "^3.4.27",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint": "^9.4.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-promise": "^6.2.0",
|
||||||
"eslint-plugin-promise": "^6.1.1",
|
"eslint-plugin-vue-scoped-css": "^2.8.0",
|
||||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
"jsdom": "^24.1.0",
|
||||||
"eslint-plugin-vue": "^9.20.1",
|
"prettier": "^3.3.0",
|
||||||
"eslint-plugin-vue-scoped-css": "^2.7.2",
|
"replace-in-file": "^7.2.0",
|
||||||
"jsdom": "^24.0.0",
|
|
||||||
"prettier": "^3.2.4",
|
|
||||||
"replace-in-file": "^7.1.0",
|
|
||||||
"tinycolor2": "^1.6.0",
|
"tinycolor2": "^1.6.0",
|
||||||
"typescript": "5.4.5",
|
"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",
|
"unplugin-vue-components": "^0.26.0",
|
||||||
"vite": "^5.0.12",
|
"vite": "^5.2.12",
|
||||||
"vite-plugin-prismjs": "^0.0.11",
|
"vite-plugin-prismjs": "^0.0.11",
|
||||||
"vite-plugin-windicss": "^1.9.3",
|
"vite-plugin-windicss": "^1.9.3",
|
||||||
"vite-svg-loader": "^5.1.0",
|
"vite-svg-loader": "^5.1.0",
|
||||||
"vitest": "^1.5.0",
|
"vitest": "^1.6.0",
|
||||||
"vue-eslint-parser": "^9.4.0",
|
"vue-tsc": "^2.0.19",
|
||||||
"vue-tsc": "^2.0.0",
|
|
||||||
"windicss": "^3.5.6"
|
"windicss": "^3.5.6"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"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 { notify } = useNotifications();
|
||||||
const i18n = useI18n();
|
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) => {
|
apiClient.setErrorHandler((err) => {
|
||||||
if (err.status === 404) {
|
if (err.status === 404) {
|
||||||
notify({ title: i18n.t('errors.not_found'), type: 'error' });
|
notify({ title: i18n.t('errors.not_found'), type: 'error' });
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
"search": "Search…",
|
"search": "Search…",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"url": "URL",
|
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"unknown_error": "An unknown error occurred",
|
"unknown_error": "An unknown error occurred",
|
||||||
"documentation_for": "Documentation for \"{topic}\"",
|
"documentation_for": "Documentation for \"{topic}\"",
|
||||||
|
@ -25,11 +24,6 @@
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"template": "MMM D, YYYY, HH:mm z",
|
"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"
|
"not_started": "not started yet"
|
||||||
},
|
},
|
||||||
"repo": {
|
"repo": {
|
||||||
|
@ -65,12 +59,10 @@
|
||||||
"user_none": "This organization / user does not have any projects yet.",
|
"user_none": "This organization / user does not have any projects yet.",
|
||||||
"not_allowed": "You are not allowed to access this repository",
|
"not_allowed": "You are not allowed to access this repository",
|
||||||
"enable": {
|
"enable": {
|
||||||
"reload": "Reload repositories",
|
|
||||||
"enable": "Enable",
|
"enable": "Enable",
|
||||||
"enabled": "Already enabled",
|
"enabled": "Already enabled",
|
||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"success": "Repository enabled",
|
"success": "Repository enabled"
|
||||||
"list_reloaded": "Repository list reloaded"
|
|
||||||
},
|
},
|
||||||
"open_in_forge": "Open repository in forge",
|
"open_in_forge": "Open repository in forge",
|
||||||
"settings": {
|
"settings": {
|
||||||
|
@ -209,7 +201,6 @@
|
||||||
"tasks": "Tasks",
|
"tasks": "Tasks",
|
||||||
"config": "Config",
|
"config": "Config",
|
||||||
"files": "Changed files ({files})",
|
"files": "Changed files ({files})",
|
||||||
"no_files": "No files have been changed.",
|
|
||||||
"no_pipelines": "No pipelines have been started yet.",
|
"no_pipelines": "No pipelines have been started yet.",
|
||||||
"no_pipeline_steps": "No pipeline steps available!",
|
"no_pipeline_steps": "No pipeline steps available!",
|
||||||
"step_not_started": "This step hasn't started yet.",
|
"step_not_started": "This step hasn't started yet.",
|
||||||
|
@ -444,7 +435,6 @@
|
||||||
"events": "Available at following events",
|
"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."
|
"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",
|
"edit": "Edit secret",
|
||||||
"delete": "Delete secret"
|
"delete": "Delete secret"
|
||||||
},
|
},
|
||||||
|
|
|
@ -147,7 +147,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import { useDate } from '~/compositions/useDate';
|
import { useDate } from '~/compositions/useDate';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import { Agent } from '~/lib/api/types';
|
import type { Agent } from '~/lib/api/types';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
|
@ -175,14 +175,14 @@ const { doSubmit: saveAgent, isLoading: isSaving } = useAsyncAction(async () =>
|
||||||
selectedAgent.value = await apiClient.createAgent(selectedAgent.value);
|
selectedAgent.value = await apiClient.createAgent(selectedAgent.value);
|
||||||
}
|
}
|
||||||
notifications.notify({
|
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',
|
type: 'success',
|
||||||
});
|
});
|
||||||
resetPage();
|
resetPage();
|
||||||
});
|
});
|
||||||
|
|
||||||
const { doSubmit: deleteAgent, isLoading: isDeleting } = useAsyncAction(async (_agent: Agent) => {
|
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'))) {
|
if (!confirm(t('admin.settings.agents.delete_confirm'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,9 @@
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="underline"
|
class="underline"
|
||||||
>{{ version.latest }}</a
|
|
||||||
>
|
>
|
||||||
|
{{ version.latest }}
|
||||||
|
</a>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ version.latest }}
|
{{ version.latest }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -43,7 +43,7 @@ import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import { Org } from '~/lib/api/types';
|
import type { Org } from '~/lib/api/types';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
|
@ -56,7 +56,7 @@ async function loadOrgs(page: number): Promise<Org[] | null> {
|
||||||
const { resetPage, data: orgs } = usePagination(loadOrgs);
|
const { resetPage, data: orgs } = usePagination(loadOrgs);
|
||||||
|
|
||||||
const { doSubmit: deleteOrg, isLoading: isDeleting } = useAsyncAction(async (_org: Org) => {
|
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'))) {
|
if (!confirm(t('admin.settings.orgs.delete_confirm'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ import ListItem from '~/components/atomic/ListItem.vue';
|
||||||
import Settings from '~/components/layout/Settings.vue';
|
import Settings from '~/components/layout/Settings.vue';
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
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';
|
import AdminQueueStats from './queue/AdminQueueStats.vue';
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
</div>
|
</div>
|
||||||
</ListItem>
|
</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>
|
</div>
|
||||||
</Settings>
|
</Settings>
|
||||||
</template>
|
</template>
|
||||||
|
@ -49,7 +49,7 @@ import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import { Repo } from '~/lib/api/types';
|
import type { Repo } from '~/lib/api/types';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
|
|
|
@ -41,7 +41,8 @@ import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
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> = {
|
const emptySecret: Partial<Secret> = {
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -74,7 +75,7 @@ const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async ()
|
||||||
await apiClient.createGlobalSecret(selectedSecret.value);
|
await apiClient.createGlobalSecret(selectedSecret.value);
|
||||||
}
|
}
|
||||||
notifications.notify({
|
notifications.notify({
|
||||||
title: i18n.t(isEditingSecret.value ? 'secrets.saved' : 'secrets.created'),
|
title: isEditingSecret.value ? i18n.t('secrets.saved') : i18n.t('secrets.created'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
selectedSecret.value = undefined;
|
selectedSecret.value = undefined;
|
||||||
|
|
|
@ -97,7 +97,7 @@ import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import { User } from '~/lib/api/types';
|
import type { User } from '~/lib/api/types';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
|
@ -135,7 +135,7 @@ const { doSubmit: saveUser, isLoading: isSaving } = useAsyncAction(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const { doSubmit: deleteUser, isLoading: isDeleting } = useAsyncAction(async (_user: User) => {
|
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'))) {
|
if (!confirm(t('admin.settings.users.delete_confirm'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,14 +54,14 @@
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import { QueueStats } from '~/lib/api/types/queue';
|
import type { QueueStats } from '~/lib/api/types/queue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
stats?: QueueStats;
|
stats?: QueueStats;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const total = computed(() => {
|
const total = computed(() => {
|
||||||
if (!props.stats) {
|
if (!props.stats) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -36,9 +36,10 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, useAttrs } from 'vue';
|
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(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<a
|
<a
|
||||||
:href="`${docsUrl}`"
|
:href="`${docsUrl}`"
|
||||||
:title="$t('documentation_for', { topic: topic })"
|
:title="$t('documentation_for', { topic })"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="text-wp-link-100 hover:text-wp-link-200 cursor-pointer mt-1"
|
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>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -28,9 +28,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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<{
|
defineProps<{
|
||||||
icon?: IconNames;
|
icon?: IconNames;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { RouteLocationRaw } from 'vue-router';
|
import type { RouteLocationRaw } from 'vue-router';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
clickable?: boolean;
|
clickable?: boolean;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import '~/style/prism.css';
|
import '~/style/prism.css';
|
||||||
|
|
||||||
import Prism from 'prismjs';
|
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>;
|
declare type Data = Record<string, unknown>;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
import { computed, toRef } from 'vue';
|
import { computed, toRef } from 'vue';
|
||||||
|
|
||||||
import Checkbox from './Checkbox.vue';
|
import Checkbox from './Checkbox.vue';
|
||||||
import { CheckboxOption } from './form.types';
|
import type { CheckboxOption } from './form.types';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue?: CheckboxOption['value'][];
|
modelValue?: CheckboxOption['value'][];
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
<div class="flex items-center mb-2">
|
<div class="flex items-center mb-2">
|
||||||
<label class="text-wp-text-100 font-bold" :for="id" v-bind="$attrs">{{ label }}</label>
|
<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" />
|
<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>
|
</div>
|
||||||
<slot :id="id" />
|
<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" />
|
<slot name="description" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,7 +20,7 @@ const modelValue = toRef(props, 'modelValue');
|
||||||
const innerValue = computed({
|
const innerValue = computed({
|
||||||
get: () => modelValue.value.toString(),
|
get: () => modelValue.value.toString(),
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
emit('update:modelValue', parseFloat(value));
|
emit('update:modelValue', Number.parseFloat(value));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, toRef } from 'vue';
|
import { computed, toRef } from 'vue';
|
||||||
|
|
||||||
import { RadioOption } from './form.types';
|
import type { RadioOption } from './form.types';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string;
|
modelValue: string;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, toRef } from 'vue';
|
import { computed, toRef } from 'vue';
|
||||||
|
|
||||||
import { SelectOption } from './form.types';
|
import type { SelectOption } from './form.types';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string;
|
modelValue: string;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
export type SelectOption = {
|
export interface SelectOption {
|
||||||
value: string;
|
value: string;
|
||||||
text: string;
|
text: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type RadioOption = SelectOption;
|
export type RadioOption = SelectOption;
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
import { IconNames } from '~/components/atomic/Icon.vue';
|
import type { IconNames } from '~/components/atomic/Icon.vue';
|
||||||
import { Tab, useTabsClient } from '~/compositions/useTabs';
|
import { useTabsClient, type Tab } from '~/compositions/useTabs';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id?: string;
|
id?: string;
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { Tab, useTabsClient } from '~/compositions/useTabs';
|
import { useTabsClient, type Tab } from '~/compositions/useTabs';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { cloneDeep } from 'lodash';
|
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 { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Button from '~/components/atomic/Button.vue';
|
import Button from '~/components/atomic/Button.vue';
|
||||||
|
@ -36,7 +36,7 @@ import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
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> = {
|
const emptySecret: Partial<Secret> = {
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -78,7 +78,7 @@ const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async ()
|
||||||
await apiClient.createOrgSecret(org.value.id, selectedSecret.value);
|
await apiClient.createOrgSecret(org.value.id, selectedSecret.value);
|
||||||
}
|
}
|
||||||
notifications.notify({
|
notifications.notify({
|
||||||
title: i18n.t(isEditingSecret.value ? 'secrets.saved' : 'secrets.created'),
|
title: isEditingSecret.value ? i18n.t('secrets.saved') : i18n.t('secrets.created'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
selectedSecret.value = undefined;
|
selectedSecret.value = undefined;
|
||||||
|
|
|
@ -8,8 +8,10 @@
|
||||||
params: { repoId: pipeline.repo_id },
|
params: { repoId: pipeline.repo_id },
|
||||||
}"
|
}"
|
||||||
class="underline"
|
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>
|
<span class="whitespace-nowrap overflow-hidden overflow-ellipsis" :title="message">{{ title }}</span>
|
||||||
<div class="flex flex-col mt-2">
|
<div class="flex flex-col mt-2">
|
||||||
<div class="flex space-x-2 items-center" :title="created">
|
<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 Icon from '~/components/atomic/Icon.vue';
|
||||||
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
||||||
import usePipeline from '~/compositions/usePipeline';
|
import usePipeline from '~/compositions/usePipeline';
|
||||||
import { PipelineFeed } from '~/lib/api/types';
|
import type { PipelineFeed } from '~/lib/api/types';
|
||||||
import { useRepoStore } from '~/store/repos';
|
import { useRepoStore } from '~/store/repos';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -24,13 +24,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full md:w-auto md:mx-4 flex items-center min-w-0">
|
<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>
|
<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-alt-100 <md:hidden mx-2">-</span>
|
||||||
<span
|
<span
|
||||||
class="text-wp-text-100 <md:underline whitespace-nowrap overflow-hidden overflow-ellipsis"
|
class="text-wp-text-100 <md:underline whitespace-nowrap overflow-hidden overflow-ellipsis"
|
||||||
:title="message"
|
:title="message"
|
||||||
>{{ title }}</span
|
|
||||||
>
|
>
|
||||||
|
{{ title }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -75,7 +78,7 @@ import { pipelineStatusColors } from '~/components/repo/pipeline/pipeline-status
|
||||||
import PipelineRunningIcon from '~/components/repo/pipeline/PipelineRunningIcon.vue';
|
import PipelineRunningIcon from '~/components/repo/pipeline/PipelineRunningIcon.vue';
|
||||||
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
import PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
||||||
import usePipeline from '~/compositions/usePipeline';
|
import usePipeline from '~/compositions/usePipeline';
|
||||||
import { Pipeline } from '~/lib/api/types';
|
import type { Pipeline } from '~/lib/api/types';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Panel from '~/components/layout/Panel.vue';
|
import Panel from '~/components/layout/Panel.vue';
|
||||||
import PipelineItem from '~/components/repo/pipeline/PipelineItem.vue';
|
import PipelineItem from '~/components/repo/pipeline/PipelineItem.vue';
|
||||||
import { Pipeline } from '~/lib/api/types';
|
import type { Pipeline } from '~/lib/api/types';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
pipelines: Pipeline[] | undefined;
|
pipelines: Pipeline[] | undefined;
|
||||||
|
|
|
@ -60,8 +60,9 @@
|
||||||
'bg-opacity-30 bg-blue-600': isSelected(line),
|
'bg-opacity-30 bg-blue-600': isSelected(line),
|
||||||
underline: isSelected(line),
|
underline: isSelected(line),
|
||||||
}"
|
}"
|
||||||
>{{ line.number }}</a
|
|
||||||
>
|
>
|
||||||
|
{{ line.number }}
|
||||||
|
</a>
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<span
|
<span
|
||||||
class="align-top whitespace-pre-wrap break-words"
|
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-40 dark:bg-opacity-50 bg-yellow-600 dark:bg-yellow-800': line.type === 'warning',
|
||||||
'bg-opacity-30 bg-blue-600': isSelected(line),
|
'bg-opacity-30 bg-blue-600': isSelected(line),
|
||||||
}"
|
}"
|
||||||
>{{ formatTime(line.time) }}</span
|
|
||||||
>
|
>
|
||||||
|
{{ formatTime(line.time) }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -110,7 +112,7 @@ import { useStorage } from '@vueuse/core';
|
||||||
import { AnsiUp } from 'ansi_up';
|
import { AnsiUp } from 'ansi_up';
|
||||||
import { decode } from 'js-base64';
|
import { decode } from 'js-base64';
|
||||||
import { debounce } from 'lodash';
|
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 { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
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 PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
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';
|
import { findStep, isStepFinished, isStepRunning } from '~/utils/helpers';
|
||||||
|
|
||||||
type LogLine = {
|
interface LogLine {
|
||||||
index: number;
|
index: number;
|
||||||
number: number;
|
number: number;
|
||||||
text: string;
|
text: string;
|
||||||
time?: number;
|
time?: number;
|
||||||
type: 'error' | 'warning' | null;
|
type: 'error' | 'warning' | null;
|
||||||
};
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
|
@ -246,7 +248,7 @@ async function download() {
|
||||||
downloadInProgress.value = true;
|
downloadInProgress.value = true;
|
||||||
logs = await apiClient.getLogs(repo.value.id, pipeline.value.number, step.value.id);
|
logs = await apiClient.getLogs(repo.value.id, pipeline.value.number, step.value.id);
|
||||||
} catch (e) {
|
} 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;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
downloadInProgress.value = false;
|
downloadInProgress.value = false;
|
||||||
|
@ -312,7 +314,7 @@ async function deleteLogs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use proper dialog (copy-pasted from web/src/components/secrets/SecretList.vue:deleteSecret)
|
// 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'))) {
|
if (!confirm(i18n.t('repo.pipeline.log_delete_confirm'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -321,7 +323,7 @@ async function deleteLogs() {
|
||||||
await apiClient.deleteLogs(repo.value.id, pipeline.value.number, step.value.id);
|
await apiClient.deleteLogs(repo.value.id, pipeline.value.number, step.value.id);
|
||||||
log.value = [];
|
log.value = [];
|
||||||
} catch (e) {
|
} 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>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-center"
|
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
|
<Icon
|
||||||
:name="service ? 'settings' : `status-${status}`"
|
:name="service ? 'settings' : `status-${status}`"
|
||||||
|
@ -18,8 +18,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Icon from '~/components/atomic/Icon.vue';
|
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';
|
import { pipelineStatusColors } from './pipeline-status';
|
||||||
|
|
||||||
|
@ -27,4 +29,22 @@ defineProps<{
|
||||||
status: PipelineStatus;
|
status: PipelineStatus;
|
||||||
service?: boolean;
|
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>
|
</script>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { computed, toRef } from 'vue';
|
||||||
|
|
||||||
import { useDate } from '~/compositions/useDate';
|
import { useDate } from '~/compositions/useDate';
|
||||||
import { useElapsedTime } from '~/compositions/useElapsedTime';
|
import { useElapsedTime } from '~/compositions/useElapsedTime';
|
||||||
import { PipelineStep, PipelineWorkflow } from '~/lib/api/types';
|
import type { PipelineStep, PipelineWorkflow } from '~/lib/api/types';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
step?: PipelineStep;
|
step?: PipelineStep;
|
||||||
|
|
|
@ -119,7 +119,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 Badge from '~/components/atomic/Badge.vue';
|
||||||
import Icon from '~/components/atomic/Icon.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 PipelineStatusIcon from '~/components/repo/pipeline/PipelineStatusIcon.vue';
|
||||||
import PipelineStepDuration from '~/components/repo/pipeline/PipelineStepDuration.vue';
|
import PipelineStepDuration from '~/components/repo/pipeline/PipelineStepDuration.vue';
|
||||||
import usePipeline from '~/compositions/usePipeline';
|
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<{
|
const props = defineProps<{
|
||||||
pipeline: Pipeline;
|
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'> = {
|
export const pipelineStatusColors: Record<PipelineStatus, 'green' | 'gray' | 'red' | 'blue' | 'orange'> = {
|
||||||
blocked: 'gray',
|
blocked: 'gray',
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject, Ref } from 'vue';
|
import { computed, inject, type Ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ import Settings from '~/components/layout/Settings.vue';
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { Repo } from '~/lib/api/types';
|
import type { Repo } from '~/lib/api/types';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -75,7 +75,7 @@ const { doSubmit: deleteRepo, isLoading: isDeletingRepo } = useAsyncAction(async
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use proper dialog
|
// 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'))) {
|
if (!confirm(i18n.t('repo.settings.actions.delete.confirm'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,16 +41,16 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useStorage } from '@vueuse/core';
|
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 InputField from '~/components/form/InputField.vue';
|
||||||
import SelectField from '~/components/form/SelectField.vue';
|
import SelectField from '~/components/form/SelectField.vue';
|
||||||
import Settings from '~/components/layout/Settings.vue';
|
import Settings from '~/components/layout/Settings.vue';
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
import useConfig from '~/compositions/useConfig';
|
import useConfig from '~/compositions/useConfig';
|
||||||
import { usePaginate } from '~/compositions/usePaginate';
|
import { usePaginate } from '~/compositions/usePaginate';
|
||||||
import { Repo } from '~/lib/api/types';
|
import type { Repo } from '~/lib/api/types';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
const repo = inject<Ref<Repo>>('repo');
|
const repo = inject<Ref<Repo>>('repo');
|
||||||
|
|
|
@ -18,8 +18,9 @@
|
||||||
>
|
>
|
||||||
<span>{{ cron.name }}</span>
|
<span>{{ cron.name }}</span>
|
||||||
<span v-if="cron.next_exec && cron.next_exec > 0" class="ml-auto">
|
<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>
|
<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="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" />
|
<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">
|
<div v-if="isEditingCron" class="ml-auto mb-4">
|
||||||
<span v-if="selectedCron.next_exec && selectedCron.next_exec > 0" class="text-wp-text-100">
|
<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') }}:
|
{{ $t('repo.settings.crons.next_exec') }}:
|
||||||
{{ date.toLocaleString(new Date(selectedCron.next_exec * 1000)) }}
|
{{ date.toLocaleString(new Date(selectedCron.next_exec * 1000)) }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -90,7 +92,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Button from '~/components/atomic/Button.vue';
|
import Button from '~/components/atomic/Button.vue';
|
||||||
|
@ -104,7 +106,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import { useDate } from '~/compositions/useDate';
|
import { useDate } from '~/compositions/useDate';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import { Cron, Repo } from '~/lib/api/types';
|
import type { Cron, Repo } from '~/lib/api/types';
|
||||||
import router from '~/router';
|
import router from '~/router';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
|
@ -141,7 +143,7 @@ const { doSubmit: createCron, isLoading: isSaving } = useAsyncAction(async () =>
|
||||||
await apiClient.createCron(repo.value.id, selectedCron.value);
|
await apiClient.createCron(repo.value.id, selectedCron.value);
|
||||||
}
|
}
|
||||||
notifications.notify({
|
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',
|
type: 'success',
|
||||||
});
|
});
|
||||||
selectedCron.value = undefined;
|
selectedCron.value = undefined;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
<template #description>
|
<template #description>
|
||||||
<i18n-t keypath="repo.settings.general.pipeline_path.desc" tag="p" class="text-sm text-wp-text-alt-100">
|
<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>
|
<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>
|
<span class="code-box-inline px-1">/</span>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</template>
|
</template>
|
||||||
|
@ -97,13 +98,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Button from '~/components/atomic/Button.vue';
|
import Button from '~/components/atomic/Button.vue';
|
||||||
import Checkbox from '~/components/form/Checkbox.vue';
|
import Checkbox from '~/components/form/Checkbox.vue';
|
||||||
import CheckboxesField from '~/components/form/CheckboxesField.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 InputField from '~/components/form/InputField.vue';
|
||||||
import NumberField from '~/components/form/NumberField.vue';
|
import NumberField from '~/components/form/NumberField.vue';
|
||||||
import RadioField from '~/components/form/RadioField.vue';
|
import RadioField from '~/components/form/RadioField.vue';
|
||||||
|
@ -113,7 +114,7 @@ import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useAuthentication from '~/compositions/useAuthentication';
|
import useAuthentication from '~/compositions/useAuthentication';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
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';
|
import { useRepoStore } from '~/store/repos';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Button from '~/components/atomic/Button.vue';
|
import Button from '~/components/atomic/Button.vue';
|
||||||
|
@ -88,8 +88,7 @@ import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import { Repo } from '~/lib/api/types';
|
import type { Registry, Repo } from '~/lib/api/types';
|
||||||
import { Registry } from '~/lib/api/types/registry';
|
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
|
@ -124,9 +123,9 @@ const { doSubmit: createRegistry, isLoading: isSaving } = useAsyncAction(async (
|
||||||
await apiClient.createRegistry(repo.value.id, selectedRegistry.value);
|
await apiClient.createRegistry(repo.value.id, selectedRegistry.value);
|
||||||
}
|
}
|
||||||
notifications.notify({
|
notifications.notify({
|
||||||
title: i18n.t(
|
title: isEditingRegistry.value
|
||||||
isEditingRegistry.value ? 'repo.settings.registries.saved' : i18n.t('repo.settings.registries.created'),
|
? i18n.t('repo.settings.registries.saved')
|
||||||
),
|
: i18n.t('repo.settings.registries.created'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
selectedRegistry.value = undefined;
|
selectedRegistry.value = undefined;
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { cloneDeep } from 'lodash';
|
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 { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Button from '~/components/atomic/Button.vue';
|
import Button from '~/components/atomic/Button.vue';
|
||||||
|
@ -36,7 +36,7 @@ import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
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> = {
|
const emptySecret: Partial<Secret> = {
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -77,9 +77,7 @@ const { resetPage, data: _secrets } = usePagination(loadSecrets, () => !selected
|
||||||
const secrets = computed(() => {
|
const secrets = computed(() => {
|
||||||
const secretsList: Record<string, Secret & { edit?: boolean; level: 'repo' | 'org' | 'global' }> = {};
|
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']) {
|
for (const level of ['repo', 'org', 'global']) {
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
for (const secret of _secrets.value) {
|
for (const secret of _secrets.value) {
|
||||||
if (
|
if (
|
||||||
((level === 'repo' && secret.repo_id !== 0 && secret.org_id === 0) ||
|
((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);
|
await apiClient.createSecret(repo.value.id, selectedSecret.value);
|
||||||
}
|
}
|
||||||
notifications.notify({
|
notifications.notify({
|
||||||
title: i18n.t(isEditingSecret.value ? 'secrets.saved' : 'secrets.created'),
|
title: isEditingSecret.value ? i18n.t('secrets.saved') : i18n.t('secrets.created'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
selectedSecret.value = undefined;
|
selectedSecret.value = undefined;
|
||||||
|
|
|
@ -59,10 +59,10 @@ import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Button from '~/components/atomic/Button.vue';
|
import Button from '~/components/atomic/Button.vue';
|
||||||
import CheckboxesField from '~/components/form/CheckboxesField.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 InputField from '~/components/form/InputField.vue';
|
||||||
import TextField from '~/components/form/TextField.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<{
|
const props = defineProps<{
|
||||||
modelValue: Partial<Secret>;
|
modelValue: Partial<Secret>;
|
||||||
|
|
|
@ -42,7 +42,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import Badge from '~/components/atomic/Badge.vue';
|
import Badge from '~/components/atomic/Badge.vue';
|
||||||
import IconButton from '~/components/atomic/IconButton.vue';
|
import IconButton from '~/components/atomic/IconButton.vue';
|
||||||
import ListItem from '~/components/atomic/ListItem.vue';
|
import ListItem from '~/components/atomic/ListItem.vue';
|
||||||
import { Secret } from '~/lib/api/types';
|
import type { Secret } from '~/lib/api/types';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: (Secret & { edit?: boolean })[];
|
modelValue: (Secret & { edit?: boolean })[];
|
||||||
|
@ -64,8 +64,8 @@ function editSecret(secret: Secret) {
|
||||||
|
|
||||||
function deleteSecret(secret: Secret) {
|
function deleteSecret(secret: Secret) {
|
||||||
// TODO: use proper dialog
|
// 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.secrets.delete_confirm'))) {
|
if (!confirm(i18n.t('secrets.delete_confirm'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit('delete', secret);
|
emit('delete', secret);
|
||||||
|
|
|
@ -23,8 +23,9 @@
|
||||||
:href="`${address}/swagger/index.html`"
|
:href="`${address}/swagger/index.html`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="ml-4 text-wp-link-100 hover:text-wp-link-200"
|
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>
|
</template>
|
||||||
<pre class="code-box">{{ usageWithCurl }}</pre>
|
<pre class="code-box">{{ usageWithCurl }}</pre>
|
||||||
</InputField>
|
</InputField>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="ml-2">
|
<div class="ml-2">
|
||||||
<h1 class="text-xl text-wp-text-100">{{ $t('secrets.secrets') }}</h1>
|
<h1 class="text-xl text-wp-text-100">{{ $t('secrets.secrets') }}</h1>
|
||||||
<p class="text-sm text-wp-text-alt-100">
|
<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" />
|
<DocsLink :topic="$t('secrets.secrets')" url="docs/usage/secrets" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,7 +51,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useAuthentication from '~/compositions/useAuthentication';
|
import useAuthentication from '~/compositions/useAuthentication';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import { Secret, WebhookEvents } from '~/lib/api/types';
|
import { WebhookEvents, type Secret } from '~/lib/api/types';
|
||||||
|
|
||||||
const emptySecret: Partial<Secret> = {
|
const emptySecret: Partial<Secret> = {
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -92,7 +92,7 @@ const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async ()
|
||||||
await apiClient.createOrgSecret(user.org_id, selectedSecret.value);
|
await apiClient.createOrgSecret(user.org_id, selectedSecret.value);
|
||||||
}
|
}
|
||||||
notifications.notify({
|
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',
|
type: 'success',
|
||||||
});
|
});
|
||||||
selectedSecret.value = undefined;
|
selectedSecret.value = undefined;
|
||||||
|
@ -101,7 +101,7 @@ const { doSubmit: createSecret, isLoading: isSaving } = useAsyncAction(async ()
|
||||||
|
|
||||||
const { doSubmit: deleteSecret, isLoading: isDeleting } = useAsyncAction(async (_secret: Secret) => {
|
const { doSubmit: deleteSecret, isLoading: isDeleting } = useAsyncAction(async (_secret: Secret) => {
|
||||||
await apiClient.deleteOrgSecret(user.org_id, _secret.name);
|
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();
|
resetPage();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ export default (): WoodpeckerClient => {
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const server = config.rootPath;
|
const server = config.rootPath;
|
||||||
const token = null;
|
const token = null;
|
||||||
const csrf = config.csrf || null;
|
const csrf = config.csrf ?? null;
|
||||||
|
|
||||||
apiClient = new WoodpeckerClient(server, token, csrf);
|
apiClient = new WoodpeckerClient(server, token, csrf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,7 @@ import useNotifications from '~/compositions/useNotifications';
|
||||||
|
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
|
|
||||||
export type UseSubmitOptions = {
|
export function useAsyncAction<T extends unknown[]>(action: (...a: T) => void | Promise<void>) {
|
||||||
showErrorNotification: false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function useAsyncAction<T extends unknown[]>(
|
|
||||||
action: (...a: T) => void | Promise<void>,
|
|
||||||
options?: UseSubmitOptions,
|
|
||||||
) {
|
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
|
||||||
async function doSubmit(...a: T) {
|
async function doSubmit(...a: T) {
|
||||||
|
@ -23,9 +16,7 @@ export function useAsyncAction<T extends unknown[]>(
|
||||||
try {
|
try {
|
||||||
await action(...a);
|
await action(...a);
|
||||||
} catch (error) {
|
} 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;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export default () =>
|
||||||
user: useConfig().user,
|
user: useConfig().user,
|
||||||
|
|
||||||
authenticate(url?: string) {
|
authenticate(url?: string) {
|
||||||
if (url) {
|
if (url !== undefined) {
|
||||||
const config = useUserConfig();
|
const config = useUserConfig();
|
||||||
config.setUserConfig('redirectUrl', url);
|
config.setUserConfig('redirectUrl', url);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { User } from '~/lib/api/types';
|
import type { User } from '~/lib/api/types';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
@ -13,11 +13,11 @@ declare global {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default () => ({
|
export default () => ({
|
||||||
user: window.WOODPECKER_USER || null,
|
user: window.WOODPECKER_USER ?? null,
|
||||||
version: window.WOODPECKER_VERSION,
|
version: window.WOODPECKER_VERSION,
|
||||||
skipVersionCheck: window.WOODPECKER_SKIP_VERSION_CHECK || false,
|
skipVersionCheck: window.WOODPECKER_SKIP_VERSION_CHECK === true || false,
|
||||||
csrf: window.WOODPECKER_CSRF || null,
|
csrf: window.WOODPECKER_CSRF ?? null,
|
||||||
forge: window.WOODPECKER_FORGE || null,
|
forge: window.WOODPECKER_FORGE ?? null,
|
||||||
rootPath: window.WOODPECKER_ROOT_PATH || '',
|
rootPath: window.WOODPECKER_ROOT_PATH ?? '',
|
||||||
enableSwagger: window.WOODPECKER_ENABLE_SWAGGER || false,
|
enableSwagger: window.WOODPECKER_ENABLE_SWAGGER === true || false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,7 +29,7 @@ export function useDate() {
|
||||||
|
|
||||||
async function setDayjsLocale(locale: string) {
|
async function setDayjsLocale(locale: string) {
|
||||||
if (!addedLocales.includes(locale)) {
|
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);
|
dayjs.locale(l.default);
|
||||||
} else {
|
} else {
|
||||||
dayjs.locale(locale);
|
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>) {
|
export function useElapsedTime(running: Ref<boolean>, startTime: Ref<number | undefined>) {
|
||||||
const time = ref<number | undefined>(startTime.value);
|
const time = ref<number | undefined>(startTime.value);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
import useConfig from '~/compositions/useConfig';
|
import useConfig from '~/compositions/useConfig';
|
||||||
import { useTheme } from '~/compositions/useTheme';
|
import { useTheme } from '~/compositions/useTheme';
|
||||||
import { PipelineStatus } from '~/lib/api/types';
|
import type { PipelineStatus } from '~/lib/api/types';
|
||||||
|
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const darkMode = computed(() => theme.value);
|
const darkMode = computed(() => theme.value);
|
||||||
|
|
|
@ -16,9 +16,9 @@ export const i18n = createI18n({
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadLocaleMessages = async (locale: string) => {
|
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();
|
return nextTick();
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,6 @@ export const setI18nLanguage = async (lang: string): Promise<void> => {
|
||||||
await setDayjsLocale(lang);
|
await setDayjsLocale(lang);
|
||||||
};
|
};
|
||||||
|
|
||||||
loadLocaleMessages(fallbackLocale);
|
loadLocaleMessages(fallbackLocale).catch(console.error);
|
||||||
loadLocaleMessages(userLanguage);
|
loadLocaleMessages(userLanguage).catch(console.error);
|
||||||
setDayjsLocale(userLanguage);
|
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>;
|
repo: Ref<Repo>;
|
||||||
org: Ref<Org | undefined>;
|
org: Ref<Org | undefined>;
|
||||||
'org-permissions': Ref<OrgPermissions | undefined>;
|
'org-permissions': Ref<OrgPermissions | undefined>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function inject<T extends keyof InjectKeys>(key: T): InjectKeys[T] {
|
export function inject<T extends keyof InjectKeys>(key: T): InjectKeys[T] {
|
||||||
const value = vueInject<InjectKeys[T]>(key);
|
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;
|
export const notifications = Notifications;
|
||||||
|
|
||||||
function notifyError(err: unknown, args: NotificationsOptions | string = {}): void {
|
function notifyError(err: Error, args: NotificationsOptions | string = {}): void {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
||||||
const mArgs = typeof args === 'string' ? { title: args } : args;
|
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 });
|
notify({ type: 'error', ...mArgs, title });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { type Ref, watch } from 'vue';
|
import { watch, type Ref } from 'vue';
|
||||||
|
|
||||||
import { usePagination } from './usePaginate';
|
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) => {
|
export const mountComposition = (cb: () => void) => {
|
||||||
const wrapper = shallowMount({
|
const wrapper = shallowMount({
|
||||||
setup() {
|
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();
|
cb();
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
@ -119,7 +119,7 @@ describe('usePaginate', () => {
|
||||||
usePaginationComposition.nextPage();
|
usePaginationComposition.nextPage();
|
||||||
await waitForState(usePaginationComposition.loading, false);
|
await waitForState(usePaginationComposition.loading, false);
|
||||||
|
|
||||||
usePaginationComposition.resetPage();
|
void usePaginationComposition.resetPage();
|
||||||
await waitForState(usePaginationComposition.loading, false);
|
await waitForState(usePaginationComposition.loading, false);
|
||||||
|
|
||||||
expect(usePaginationComposition.data.value.length).toBe(3);
|
expect(usePaginationComposition.data.value.length).toBe(3);
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { useInfiniteScroll } from '@vueuse/core';
|
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[]> {
|
export async function usePaginate<T>(getSingle: (page: number) => Promise<T[]>): Promise<T[]> {
|
||||||
let hasMore = true;
|
let hasMore = true;
|
||||||
let page = 1;
|
let page = 1;
|
||||||
const result: T[] = [];
|
const result: T[] = [];
|
||||||
while (hasMore) {
|
while (hasMore) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
const singleRes = await getSingle(page);
|
const singleRes = await getSingle(page);
|
||||||
result.push(...singleRes);
|
result.push(...singleRes);
|
||||||
hasMore = singleRes.length !== 0;
|
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 { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import { useDate } from '~/compositions/useDate';
|
import { useDate } from '~/compositions/useDate';
|
||||||
import { useElapsedTime } from '~/compositions/useElapsedTime';
|
import { useElapsedTime } from '~/compositions/useElapsedTime';
|
||||||
import { Pipeline } from '~/lib/api/types';
|
import type { Pipeline } from '~/lib/api/types';
|
||||||
import { convertEmojis } from '~/utils/emoji';
|
import { convertEmojis } from '~/utils/emoji';
|
||||||
|
|
||||||
const { toLocaleString, timeAgo, prettyDuration } = useDate();
|
const { toLocaleString, timeAgo, prettyDuration } = useDate();
|
||||||
|
|
|
@ -5,20 +5,20 @@ import { usePipelineStore } from '~/store/pipelines';
|
||||||
|
|
||||||
import useAuthentication from './useAuthentication';
|
import useAuthentication from './useAuthentication';
|
||||||
|
|
||||||
const { userConfig, setUserConfig } = useUserConfig();
|
const userConfig = useUserConfig();
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const pipelineStore = usePipelineStore();
|
const pipelineStore = usePipelineStore();
|
||||||
const { isAuthenticated } = useAuthentication();
|
const { isAuthenticated } = useAuthentication();
|
||||||
|
|
||||||
const isOpen = computed(() => userConfig.value.isPipelineFeedOpen && !!isAuthenticated);
|
const isOpen = computed(() => userConfig.userConfig.value.isPipelineFeedOpen && !!isAuthenticated);
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
setUserConfig('isPipelineFeedOpen', !userConfig.value.isPipelineFeedOpen);
|
userConfig.setUserConfig('isPipelineFeedOpen', !userConfig.userConfig.value.isPipelineFeedOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
setUserConfig('isPipelineFeedOpen', false);
|
userConfig.setUserConfig('isPipelineFeedOpen', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedPipelines = toRef(pipelineStore, 'pipelineFeed');
|
const sortedPipelines = toRef(pipelineStore, 'pipelineFeed');
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Fuse from 'fuse.js';
|
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 .
|
* Compares Repos lexicographically using owner/name .
|
||||||
|
@ -9,7 +9,7 @@ import { Repo } from '~/lib/api/types';
|
||||||
function repoCompare(a: Repo, b: Repo) {
|
function repoCompare(a: Repo, b: Repo) {
|
||||||
const x = `${a.owner}/${a.name}`;
|
const x = `${a.owner}/${a.name}`;
|
||||||
const y = `${b.owner}/${b.name}`;
|
const y = `${b.owner}/${b.name}`;
|
||||||
// eslint-disable-next-line no-nested-ternary
|
|
||||||
return x === y ? 0 : x > y ? 1 : -1;
|
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) {
|
export function useRouteBack(to: RouteLocationRaw) {
|
||||||
const router = useRouter();
|
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';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
export type Tab = {
|
export interface Tab {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
iconClass?: string;
|
iconClass?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function useTabsProvider({
|
export function useTabsProvider({
|
||||||
activeTab,
|
activeTab,
|
||||||
|
@ -29,7 +29,7 @@ export function useTabsProvider({
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashTab = route.hash.replace(/^#/, '');
|
const hashTab = route.hash.replace(/^#/, '');
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
activeTab.value = hashTab || tabs.value[0].id;
|
activeTab.value = hashTab || tabs.value[0].id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { computed, ref } from 'vue';
|
||||||
|
|
||||||
const USER_CONFIG_KEY = 'woodpecker-user-config';
|
const USER_CONFIG_KEY = 'woodpecker-user-config';
|
||||||
|
|
||||||
type UserConfig = {
|
interface UserConfig {
|
||||||
isPipelineFeedOpen: boolean;
|
isPipelineFeedOpen: boolean;
|
||||||
redirectUrl: string;
|
redirectUrl: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
const defaultUserConfig: UserConfig = {
|
const defaultUserConfig: UserConfig = {
|
||||||
isPipelineFeedOpen: false,
|
isPipelineFeedOpen: false,
|
||||||
|
@ -14,11 +14,11 @@ const defaultUserConfig: UserConfig = {
|
||||||
|
|
||||||
function loadUserConfig(): UserConfig {
|
function loadUserConfig(): UserConfig {
|
||||||
const lsData = localStorage.getItem(USER_CONFIG_KEY);
|
const lsData = localStorage.getItem(USER_CONFIG_KEY);
|
||||||
if (!lsData) {
|
if (lsData === null) {
|
||||||
return defaultUserConfig;
|
return defaultUserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSON.parse(lsData);
|
return JSON.parse(lsData) as UserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = ref<UserConfig>(loadUserConfig());
|
const config = ref<UserConfig>(loadUserConfig());
|
||||||
|
|
|
@ -5,11 +5,11 @@ import { onMounted, ref } from 'vue';
|
||||||
import useAuthentication from './useAuthentication';
|
import useAuthentication from './useAuthentication';
|
||||||
import useConfig from './useConfig';
|
import useConfig from './useConfig';
|
||||||
|
|
||||||
type VersionInfo = {
|
interface VersionInfo {
|
||||||
latest: string;
|
latest: string;
|
||||||
rc: string;
|
rc: string;
|
||||||
next: string;
|
next: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
const version = ref<{
|
const version = ref<{
|
||||||
latest: string | undefined;
|
latest: string | undefined;
|
||||||
|
@ -22,10 +22,9 @@ const version = ref<{
|
||||||
async function fetchVersion(): Promise<VersionInfo | undefined> {
|
async function fetchVersion(): Promise<VersionInfo | undefined> {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch('https://woodpecker-ci.org/version.json');
|
const resp = await fetch('https://woodpecker-ci.org/version.json');
|
||||||
const json = await resp.json();
|
const json = (await resp.json()) as VersionInfo;
|
||||||
return json;
|
return json;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error('Failed to fetch version info', error);
|
console.error('Failed to fetch version info', error);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +44,7 @@ export function useVersion() {
|
||||||
const usesNext = current.startsWith('next');
|
const usesNext = current.startsWith('next');
|
||||||
|
|
||||||
const { user } = useAuthentication();
|
const { user } = useAuthentication();
|
||||||
if (config.skipVersionCheck || !user?.admin) {
|
if (config.skipVersionCheck || user?.admin !== true) {
|
||||||
version.value = {
|
version.value = {
|
||||||
latest: undefined,
|
latest: undefined,
|
||||||
current,
|
current,
|
||||||
|
|
|
@ -1,27 +1,28 @@
|
||||||
export type ApiError = {
|
export interface ApiError {
|
||||||
status: number;
|
status: number;
|
||||||
message: string;
|
message: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function encodeQueryString(_params: Record<string, string | number | boolean | undefined> = {}): string {
|
type QueryParams = Record<string, string | number | boolean>;
|
||||||
const params: Record<string, string | number | boolean> = {};
|
|
||||||
|
|
||||||
Object.keys(_params).forEach((key) => {
|
export function encodeQueryString(_params: unknown = {}): string {
|
||||||
const val = _params[key];
|
const __params = _params as QueryParams;
|
||||||
|
const params: QueryParams = {};
|
||||||
|
|
||||||
|
Object.keys(__params).forEach((key) => {
|
||||||
|
const val = __params[key];
|
||||||
if (val !== undefined) {
|
if (val !== undefined) {
|
||||||
params[key] = val;
|
params[key] = val;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return params
|
return Object.keys(params)
|
||||||
? Object.keys(params)
|
.sort()
|
||||||
.sort()
|
.map((key) => {
|
||||||
.map((key) => {
|
const val = params[key];
|
||||||
const val = params[key];
|
return `${encodeURIComponent(key)}=${encodeURIComponent(val)}`;
|
||||||
return `${encodeURIComponent(key)}=${encodeURIComponent(val)}`;
|
})
|
||||||
})
|
.join('&');
|
||||||
.join('&')
|
|
||||||
: '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ApiClient {
|
export default class ApiClient {
|
||||||
|
@ -43,11 +44,11 @@ export default class ApiClient {
|
||||||
const res = await fetch(`${this.server}${path}`, {
|
const res = await fetch(`${this.server}${path}`, {
|
||||||
method,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
...(method !== 'GET' && this.csrf ? { 'X-CSRF-TOKEN': this.csrf } : {}),
|
...(method !== 'GET' && this.csrf !== null ? { 'X-CSRF-TOKEN': this.csrf } : {}),
|
||||||
...(this.token ? { Authorization: `Bearer ${this.token}` } : {}),
|
...(this.token !== null ? { Authorization: `Bearer ${this.token}` } : {}),
|
||||||
...(data ? { 'Content-Type': 'application/json' } : {}),
|
...(data !== undefined ? { 'Content-Type': 'application/json' } : {}),
|
||||||
},
|
},
|
||||||
body: data ? JSON.stringify(data) : undefined,
|
body: data !== undefined ? JSON.stringify(data) : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
@ -62,7 +63,7 @@ export default class ApiClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentType = res.headers.get('Content-Type');
|
const contentType = res.headers.get('Content-Type');
|
||||||
if (contentType && contentType.startsWith('application/json')) {
|
if (contentType !== null && contentType.startsWith('application/json')) {
|
||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,15 +88,15 @@ export default class ApiClient {
|
||||||
|
|
||||||
_subscribe<T>(path: string, callback: (data: T) => void, opts = { reconnect: true }) {
|
_subscribe<T>(path: string, callback: (data: T) => void, opts = { reconnect: true }) {
|
||||||
const query = encodeQueryString({
|
const query = encodeQueryString({
|
||||||
access_token: this.token || undefined,
|
access_token: this.token ?? undefined,
|
||||||
});
|
});
|
||||||
let _path = this.server ? this.server + path : path;
|
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);
|
const events = new EventSource(_path);
|
||||||
events.onmessage = (event) => {
|
events.onmessage = (event) => {
|
||||||
const data = JSON.parse(event.data) as T;
|
const data = JSON.parse(event.data as string) as T;
|
||||||
// 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);
|
callback(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import ApiClient, { encodeQueryString } from './client';
|
import ApiClient, { encodeQueryString } from './client';
|
||||||
import {
|
import type {
|
||||||
Agent,
|
Agent,
|
||||||
Cron,
|
Cron,
|
||||||
Org,
|
Org,
|
||||||
|
@ -18,27 +18,27 @@ import {
|
||||||
User,
|
User,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
type RepoListOptions = {
|
interface RepoListOptions {
|
||||||
all?: boolean;
|
all?: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
// PipelineOptions is the data for creating a new pipeline
|
// PipelineOptions is the data for creating a new pipeline
|
||||||
type PipelineOptions = {
|
interface PipelineOptions {
|
||||||
branch: string;
|
branch: string;
|
||||||
variables: Record<string, string>;
|
variables: Record<string, string>;
|
||||||
};
|
}
|
||||||
|
|
||||||
type DeploymentOptions = {
|
interface DeploymentOptions {
|
||||||
id: string;
|
id: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
task: string;
|
task: string;
|
||||||
variables: Record<string, string>;
|
variables: Record<string, string>;
|
||||||
};
|
}
|
||||||
|
|
||||||
type PaginationOptions = {
|
interface PaginationOptions {
|
||||||
page?: number;
|
page?: number;
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default class WoodpeckerClient extends ApiClient {
|
export default class WoodpeckerClient extends ApiClient {
|
||||||
getRepoList(opts?: RepoListOptions): Promise<Repo[]> {
|
getRepoList(opts?: RepoListOptions): Promise<Repo[]> {
|
||||||
|
@ -336,10 +336,10 @@ export default class WoodpeckerClient extends ApiClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
repairAllRepos(): Promise<unknown> {
|
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 {
|
on(callback: (data: { pipeline?: Pipeline; repo?: Repo }) => void): EventSource {
|
||||||
return this._subscribe('/api/stream/events', callback, {
|
return this._subscribe('/api/stream/events', callback, {
|
||||||
reconnect: true,
|
reconnect: true,
|
||||||
|
@ -350,7 +350,7 @@ export default class WoodpeckerClient extends ApiClient {
|
||||||
repoId: number,
|
repoId: number,
|
||||||
pipeline: number,
|
pipeline: number,
|
||||||
step: 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,
|
callback: (data: PipelineLog) => void,
|
||||||
): EventSource {
|
): EventSource {
|
||||||
return this._subscribe(`/api/stream/logs/${repoId}/${pipeline}/${step}`, callback, {
|
return this._subscribe(`/api/stream/logs/${repoId}/${pipeline}/${step}`, callback, {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export type Agent = {
|
export interface Agent {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
token: string;
|
token: string;
|
||||||
|
@ -10,4 +10,4 @@ export type Agent = {
|
||||||
capacity: number;
|
capacity: number;
|
||||||
version: string;
|
version: string;
|
||||||
no_schedule: boolean;
|
no_schedule: boolean;
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export type Cron = {
|
export interface Cron {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
branch: string;
|
branch: string;
|
||||||
schedule: string;
|
schedule: string;
|
||||||
next_exec: number;
|
next_exec: number;
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// A version control organization.
|
// A version control organization.
|
||||||
export type Org = {
|
export interface Org {
|
||||||
// The name of the organization.
|
// The name of the organization.
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
is_user: boolean;
|
is_user: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type OrgPermissions = {
|
export interface OrgPermissions {
|
||||||
member: boolean;
|
member: boolean;
|
||||||
admin: 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;
|
type: string;
|
||||||
message: string;
|
message: string;
|
||||||
data?: D;
|
data?: D;
|
||||||
is_warning: boolean;
|
is_warning: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
// A pipeline for a repository.
|
// A pipeline for a repository.
|
||||||
export type Pipeline = {
|
export interface Pipeline {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
// The pipeline number.
|
// The pipeline number.
|
||||||
|
@ -89,7 +89,7 @@ export type Pipeline = {
|
||||||
workflows?: PipelineWorkflow[];
|
workflows?: PipelineWorkflow[];
|
||||||
|
|
||||||
changed_files?: string[];
|
changed_files?: string[];
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PipelineStatus =
|
export type PipelineStatus =
|
||||||
| 'blocked'
|
| 'blocked'
|
||||||
|
@ -103,7 +103,7 @@ export type PipelineStatus =
|
||||||
| 'started'
|
| 'started'
|
||||||
| 'success';
|
| 'success';
|
||||||
|
|
||||||
export type PipelineWorkflow = {
|
export interface PipelineWorkflow {
|
||||||
id: number;
|
id: number;
|
||||||
pipeline_id: number;
|
pipeline_id: number;
|
||||||
pid: number;
|
pid: number;
|
||||||
|
@ -115,9 +115,9 @@ export type PipelineWorkflow = {
|
||||||
agent_id?: number;
|
agent_id?: number;
|
||||||
error?: string;
|
error?: string;
|
||||||
children: PipelineStep[];
|
children: PipelineStep[];
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PipelineStep = {
|
export interface PipelineStep {
|
||||||
id: number;
|
id: number;
|
||||||
uuid: string;
|
uuid: string;
|
||||||
pipeline_id: number;
|
pipeline_id: number;
|
||||||
|
@ -130,21 +130,22 @@ export type PipelineStep = {
|
||||||
end_time?: number;
|
end_time?: number;
|
||||||
error?: string;
|
error?: string;
|
||||||
type?: StepType;
|
type?: StepType;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PipelineLog = {
|
export interface PipelineLog {
|
||||||
id: number;
|
id: number;
|
||||||
step_id: number;
|
step_id: number;
|
||||||
time: number;
|
time: number;
|
||||||
line: number;
|
line: number;
|
||||||
data: string; // base64 encoded
|
data: string; // base64 encoded
|
||||||
type: number;
|
type: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type PipelineFeed = Pipeline & {
|
export type PipelineFeed = Pipeline & {
|
||||||
repo_id: number;
|
repo_id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
export enum StepType {
|
export enum StepType {
|
||||||
Clone = 'clone',
|
Clone = 'clone',
|
||||||
Service = 'service',
|
Service = 'service',
|
||||||
|
@ -152,3 +153,4 @@ export enum StepType {
|
||||||
Commands = 'commands',
|
Commands = 'commands',
|
||||||
Cache = 'cache',
|
Cache = 'cache',
|
||||||
}
|
}
|
||||||
|
/* eslint-enable */
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// A config for a pipeline.
|
// A config for a pipeline.
|
||||||
export type PipelineConfig = {
|
export interface PipelineConfig {
|
||||||
hash: string;
|
hash: string;
|
||||||
name: string;
|
name: string;
|
||||||
data: string;
|
data: string;
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// A version control pull request.
|
// A version control pull request.
|
||||||
export type PullRequest = {
|
export interface PullRequest {
|
||||||
// The index of the pull request.
|
// The index of the pull request.
|
||||||
index: string;
|
index: string;
|
||||||
// The title of the pull request.
|
// The title of the pull request.
|
||||||
title: string;
|
title: string;
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export type Task = {
|
export interface Task {
|
||||||
id: number;
|
id: number;
|
||||||
data: string;
|
data: string;
|
||||||
labels: { [key: string]: string };
|
labels: { [key: string]: string };
|
||||||
|
@ -6,20 +6,20 @@ export type Task = {
|
||||||
dep_status: { [key: string]: string };
|
dep_status: { [key: string]: string };
|
||||||
run_on: string[];
|
run_on: string[];
|
||||||
agent_id: number;
|
agent_id: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type QueueStats = {
|
export interface QueueStats {
|
||||||
worker_count: number;
|
worker_count: number;
|
||||||
pending_count: number;
|
pending_count: number;
|
||||||
waiting_on_deps_count: number;
|
waiting_on_deps_count: number;
|
||||||
running_count: number;
|
running_count: number;
|
||||||
completed_count: number;
|
completed_count: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type QueueInfo = {
|
export interface QueueInfo {
|
||||||
pending: Task[];
|
pending: Task[];
|
||||||
waiting_on_deps: Task[];
|
waiting_on_deps: Task[];
|
||||||
running: Task[];
|
running: Task[];
|
||||||
stats: QueueStats;
|
stats: QueueStats;
|
||||||
paused: boolean;
|
paused: boolean;
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export type Registry = {
|
export interface Registry {
|
||||||
id: string;
|
id: string;
|
||||||
address: string;
|
address: string;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// A version control repository.
|
// A version control repository.
|
||||||
export type Repo = {
|
export interface Repo {
|
||||||
// Is the repo currently active or not
|
// Is the repo currently active or not
|
||||||
active: boolean;
|
active: boolean;
|
||||||
|
|
||||||
|
@ -70,13 +70,15 @@ export type Repo = {
|
||||||
cancel_previous_pipeline_events: string[];
|
cancel_previous_pipeline_events: string[];
|
||||||
|
|
||||||
netrc_only_trusted: boolean;
|
netrc_only_trusted: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
export enum RepoVisibility {
|
export enum RepoVisibility {
|
||||||
Public = 'public',
|
Public = 'public',
|
||||||
Private = 'private',
|
Private = 'private',
|
||||||
Internal = 'internal',
|
Internal = 'internal',
|
||||||
}
|
}
|
||||||
|
/* eslint-enable */
|
||||||
|
|
||||||
export type RepoSettings = Pick<
|
export type RepoSettings = Pick<
|
||||||
Repo,
|
Repo,
|
||||||
|
@ -91,9 +93,9 @@ export type RepoSettings = Pick<
|
||||||
| 'netrc_only_trusted'
|
| 'netrc_only_trusted'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type RepoPermissions = {
|
export interface RepoPermissions {
|
||||||
pull: boolean;
|
pull: boolean;
|
||||||
push: boolean;
|
push: boolean;
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
synced: number;
|
synced: number;
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { WebhookEvents } from './webhook';
|
import type { WebhookEvents } from './webhook';
|
||||||
|
|
||||||
export type Secret = {
|
export interface Secret {
|
||||||
id: string;
|
id: string;
|
||||||
repo_id: number;
|
repo_id: number;
|
||||||
org_id: number;
|
org_id: number;
|
||||||
|
@ -8,4 +8,4 @@ export type Secret = {
|
||||||
value: string;
|
value: string;
|
||||||
events: WebhookEvents[];
|
events: WebhookEvents[];
|
||||||
images: string[];
|
images: string[];
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// The user account.
|
// The user account.
|
||||||
export type User = {
|
export interface User {
|
||||||
id: number;
|
id: number;
|
||||||
// The unique identifier for the account.
|
// The unique identifier for the account.
|
||||||
|
|
||||||
|
@ -20,4 +20,4 @@ export type User = {
|
||||||
|
|
||||||
org_id: number;
|
org_id: number;
|
||||||
// The ID of the org assigned to the user.
|
// The ID of the org assigned to the user.
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
export enum WebhookEvents {
|
export enum WebhookEvents {
|
||||||
Push = 'push',
|
Push = 'push',
|
||||||
Tag = 'tag',
|
Tag = 'tag',
|
||||||
|
@ -8,3 +9,4 @@ export enum WebhookEvents {
|
||||||
Cron = 'cron',
|
Cron = 'cron',
|
||||||
Manual = 'manual',
|
Manual = 'manual',
|
||||||
}
|
}
|
||||||
|
/* eslint-enable */
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { i18n } from '~/compositions/useI18n';
|
||||||
import { notifications } from '~/compositions/useNotifications';
|
import { notifications } from '~/compositions/useNotifications';
|
||||||
import router from '~/router';
|
import router from '~/router';
|
||||||
|
|
||||||
|
// eslint-disable-next-line ts/no-unsafe-argument
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
|
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
import useAuthentication from '~/compositions/useAuthentication';
|
import useAuthentication from '~/compositions/useAuthentication';
|
||||||
import useConfig from '~/compositions/useConfig';
|
import useConfig from '~/compositions/useConfig';
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { defineStore } from 'pinia';
|
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 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 { useRepoStore } from '~/store/repos';
|
||||||
import { comparePipelines, comparePipelinesWithStatus, isPipelineActive } from '~/utils/helpers';
|
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());
|
const pipelines: Map<number, Map<number, Pipeline>> = reactive(new Map());
|
||||||
|
|
||||||
function setPipeline(repoId: number, pipeline: Pipeline) {
|
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.set(pipeline.number, {
|
||||||
...(repoPipelines.get(pipeline.number) || {}),
|
...(repoPipelines.get(pipeline.number) || {}),
|
||||||
...pipeline,
|
...pipeline,
|
||||||
|
@ -27,7 +27,7 @@ export const usePipelineStore = defineStore('pipelines', () => {
|
||||||
|
|
||||||
function getPipeline(repoId: Ref<number>, _pipelineNumber: Ref<string>) {
|
function getPipeline(repoId: Ref<number>, _pipelineNumber: Ref<string>) {
|
||||||
return computed(() => {
|
return computed(() => {
|
||||||
const pipelineNumber = parseInt(_pipelineNumber.value, 10);
|
const pipelineNumber = Number.parseInt(_pipelineNumber.value, 10);
|
||||||
return pipelines.get(repoId.value)?.get(pipelineNumber);
|
return pipelines.get(repoId.value)?.get(pipelineNumber);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { defineStore } from 'pinia';
|
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 useApiClient from '~/compositions/useApiClient';
|
||||||
import { Repo } from '~/lib/api/types';
|
import type { Repo } from '~/lib/api/types';
|
||||||
|
|
||||||
export const useRepoStore = defineStore('repos', () => {
|
export const useRepoStore = defineStore('repos', () => {
|
||||||
const apiClient = useApiClient();
|
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 {
|
export function findStep(workflows: PipelineWorkflow[], pid: number): PipelineStep | undefined {
|
||||||
return workflows.reduce(
|
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} true if the process is in a completed state
|
||||||
* @param {Object} step - The process object.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
*/
|
||||||
export function isStepFinished(step: PipelineStep): boolean {
|
export function isStepFinished(step: PipelineStep): boolean {
|
||||||
return step.state !== 'running' && step.state !== 'pending';
|
return step.state !== 'running' && step.state !== 'pending';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the process is running.
|
* @param {object} step - The process object.
|
||||||
*
|
* @returns {boolean} true if the process is running
|
||||||
* @param {Object} step - The process object.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
*/
|
||||||
export function isStepRunning(step: PipelineStep): boolean {
|
export function isStepRunning(step: PipelineStep): boolean {
|
||||||
return step.state === 'running';
|
return step.state === 'running';
|
||||||
|
@ -45,9 +41,9 @@ export function isStepRunning(step: PipelineStep): boolean {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two pipelines by creation timestamp.
|
* Compare two pipelines by creation timestamp.
|
||||||
* @param {Object} a - A pipeline.
|
* @param {object} a - A pipeline.
|
||||||
* @param {Object} b - A pipeline.
|
* @param {object} b - A pipeline.
|
||||||
* @returns {number}
|
* @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 {
|
export function comparePipelines(a: Pipeline, b: Pipeline): number {
|
||||||
return (b.created_at || -1) - (a.created_at || -1);
|
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.
|
* Compare two pipelines by the status.
|
||||||
* Giving pending, running, or started higher priority than other status
|
* Giving pending, running, or started higher priority than other status
|
||||||
* @param {Object} a - A pipeline.
|
* @param {object} a - A pipeline.
|
||||||
* @param {Object} b - A pipeline.
|
* @param {object} b - A pipeline.
|
||||||
* @returns {number}
|
* @returns {number} 0 if status same priority, < 0 if b has higher priority, > 0 otherwise
|
||||||
*/
|
*/
|
||||||
export function comparePipelinesWithStatus(a: Pipeline, b: Pipeline): number {
|
export function comparePipelinesWithStatus(a: Pipeline, b: Pipeline): number {
|
||||||
const bPriority = ['pending', 'running', 'started'].includes(b.status) ? 1 : 0;
|
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);
|
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 {
|
export function repoSlug(ownerOrRepo: string | Repo, name?: string): string {
|
||||||
if (typeof ownerOrRepo === 'string') {
|
if (typeof ownerOrRepo === 'string') {
|
||||||
if (!name) {
|
if (name === undefined) {
|
||||||
throw new Error('Please provide a name as well');
|
throw new Error('Please provide a name as well');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col h-full w-full items-center justify-center">
|
<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>
|
<p class="text-2xl mb-8">{{ $t('not_found.not_found') }}</p>
|
||||||
<span
|
<router-link class="text-blue-400" replace :to="{ name: 'home' }">
|
||||||
><router-link class="text-blue-400" replace :to="{ name: 'home' }">{{
|
{{ $t('not_found.back_home') }}
|
||||||
$t('not_found.back_home')
|
</router-link>
|
||||||
}}</router-link></span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -45,7 +45,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { useRepoSearch } from '~/compositions/useRepoSearch';
|
import { useRepoSearch } from '~/compositions/useRepoSearch';
|
||||||
import { useRouteBack } from '~/compositions/useRouteBack';
|
import { useRouteBack } from '~/compositions/useRouteBack';
|
||||||
import { Repo } from '~/lib/api/types';
|
import type { Repo } from '~/lib/api/types';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
|
|
|
@ -8,13 +8,12 @@ import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
orgName: string;
|
orgName: string;
|
||||||
}>();
|
}>();
|
||||||
|
const apiClient = useApiClient();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const org = await apiClient.lookupOrg(props.orgName);
|
const org = await apiClient.lookupOrg(props.orgName);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
<span>
|
<span>
|
||||||
<router-link :to="{ name: 'org' }" class="hover:underline">
|
<router-link :to="{ name: 'org' }" class="hover:underline">
|
||||||
{{ org.name }}
|
{{ org.name }}
|
||||||
|
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||||
</router-link>
|
</router-link>
|
||||||
/
|
/
|
||||||
{{ $t('settings') }}
|
{{ $t('settings') }}
|
||||||
|
|
|
@ -25,13 +25,13 @@ import IconButton from '~/components/atomic/IconButton.vue';
|
||||||
import Scaffold from '~/components/layout/scaffold/Scaffold.vue';
|
import Scaffold from '~/components/layout/scaffold/Scaffold.vue';
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
import { provide } from '~/compositions/useInjectProvide';
|
import { provide } from '~/compositions/useInjectProvide';
|
||||||
import { Org, OrgPermissions } from '~/lib/api/types';
|
import type { Org, OrgPermissions } from '~/lib/api/types';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
orgId: string;
|
orgId: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const orgId = computed(() => parseInt(props.orgId, 10));
|
const orgId = computed(() => Number.parseInt(props.orgId, 10));
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
|
|
||||||
const org = ref<Org>();
|
const org = ref<Org>();
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 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<{
|
const props = defineProps<{
|
||||||
branch: string;
|
branch: string;
|
||||||
|
|
|
@ -21,13 +21,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 Badge from '~/components/atomic/Badge.vue';
|
||||||
import ListItem from '~/components/atomic/ListItem.vue';
|
import ListItem from '~/components/atomic/ListItem.vue';
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import { Repo } from '~/lib/api/types';
|
import type { Repo } from '~/lib/api/types';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,13 @@ import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
repoOwner: string;
|
repoOwner: string;
|
||||||
repoName: string;
|
repoName: string;
|
||||||
}>();
|
}>();
|
||||||
|
const apiClient = useApiClient();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const repo = await apiClient.lookupRepo(props.repoOwner, props.repoName);
|
const repo = await apiClient.lookupRepo(props.repoOwner, props.repoName);
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, Ref } from 'vue';
|
import { inject, type Ref } from 'vue';
|
||||||
|
|
||||||
import PipelineList from '~/components/repo/pipeline/PipelineList.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 repo = inject<Ref<Repo>>('repo');
|
||||||
const repoPermissions = inject<Ref<RepoPermissions>>('repo-permissions');
|
const repoPermissions = inject<Ref<RepoPermissions>>('repo-permissions');
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 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<{
|
const props = defineProps<{
|
||||||
pullRequest: string;
|
pullRequest: string;
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
class="text-wp-text-100"
|
class="text-wp-text-100"
|
||||||
:to="{ name: 'repo-pull-request', params: { pullRequest: pullRequest.index } }"
|
: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>
|
<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-alt-100 <md:hidden mx-2">-</span>
|
||||||
<span class="text-wp-text-100 <md:underline whitespace-nowrap overflow-hidden overflow-ellipsis">{{
|
<span class="text-wp-text-100 <md:underline whitespace-nowrap overflow-hidden overflow-ellipsis">{{
|
||||||
pullRequest.title
|
pullRequest.title
|
||||||
|
@ -24,14 +26,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 Icon from '~/components/atomic/Icon.vue';
|
||||||
import ListItem from '~/components/atomic/ListItem.vue';
|
import ListItem from '~/components/atomic/ListItem.vue';
|
||||||
import Panel from '~/components/layout/Panel.vue';
|
import Panel from '~/components/layout/Panel.vue';
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import { PullRequest, Repo } from '~/lib/api/types';
|
import type { PullRequest, Repo } from '~/lib/api/types';
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
<span>
|
<span>
|
||||||
<router-link :to="{ name: 'org', params: { orgId: repo!.org_id } }" class="hover:underline">
|
<router-link :to="{ name: 'org', params: { orgId: repo!.org_id } }" class="hover:underline">
|
||||||
{{ repo!.owner }}
|
{{ repo!.owner }}
|
||||||
|
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||||
</router-link>
|
</router-link>
|
||||||
/
|
/
|
||||||
<router-link :to="{ name: 'repo' }" class="hover:underline">
|
<router-link :to="{ name: 'repo' }" class="hover:underline">
|
||||||
{{ repo!.name }}
|
{{ repo!.name }}
|
||||||
|
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||||
</router-link>
|
</router-link>
|
||||||
/
|
/
|
||||||
{{ $t('settings') }}
|
{{ $t('settings') }}
|
||||||
|
@ -36,7 +38,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, onMounted, Ref } from 'vue';
|
import { inject, onMounted, type Ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
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 SecretsTab from '~/components/repo/settings/SecretsTab.vue';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { useRouteBack } from '~/compositions/useRouteBack';
|
import { useRouteBack } from '~/compositions/useRouteBack';
|
||||||
import { Repo, RepoPermissions } from '~/lib/api/types';
|
import type { Repo, RepoPermissions } from '~/lib/api/types';
|
||||||
|
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
const router = useRouter();
|
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