Compare commits

..

16 Commits

Author SHA1 Message Date
elee 0cd11ce578 debug env
agola/agola-web/agola web build/test The run finished successfully Details
2022-03-04 03:14:31 -06:00
elee 583dd58e20 switch to hyphen:
agola/agola-web/agola web build/test The run failed Details
2022-03-04 03:01:21 -06:00
elee 4d32019249 zzz
agola/agola-web/agola web build/test The run finished successfully Details
2022-03-04 02:48:21 -06:00
elee 81a2faf4ab forc
agola/agola-web/agola web build/test The run failed Details
2022-03-04 02:47:04 -06:00
elee d1b067e81b maybe now?
agola/agola-web/agola web build/test The run failed Details
2022-03-04 02:34:07 -06:00
elee 9c4598973d maybe fix?
agola/agola-web/agola web build/test The run failed Details
2022-03-04 02:25:35 -06:00
elee 8794196988 fix
agola/agola-web/agola web build/test The run failed Details
2022-03-04 02:17:39 -06:00
elee 7de411662d test tuxpaint
agola/agola-web/agola web build/test The run failed Details
2022-03-04 02:04:34 -06:00
Simone Gotti 77f7af09ab
Merge pull request #59 from sgotti/format_with_prettier
agola/agola-web/agola web build/test The run is pending Details
*: Format with prettier
2022-02-24 09:48:46 +01:00
Simone Gotti 3f2c57394a *: Format with prettier
Use latest prettier as devDependency so tools will use (or can be
configured to use) the npm provided version.
The unique config change is to use single quotes instead of double
quotes.
2022-02-24 09:02:02 +01:00
Simone Gotti ca0c494425
Merge pull request #60 from sgotti/fix_docker_node_image_version
Dockerfile: use fixed node image version
2022-02-24 09:01:40 +01:00
Simone Gotti 5086f0a4ec Dockerfile: use fixed node image version
Fix it to node 12 until we migrate to new versions of tailwind, vue
etc...
2022-02-24 08:54:03 +01:00
Simone Gotti 3e6f24b14b
Merge pull request #48 from Pauloo27/newsource
Add "create remote source" page
2021-08-10 16:33:13 +02:00
Paulo 64c9a807c7
feat: add create remote source form 2021-07-22 00:28:12 -03:00
Simone Gotti 2adcbc2044
Merge pull request #47 from Pauloo27/home
Add simple content to home page
2021-07-15 09:38:50 +02:00
Paulo a243a96cd7
home: add content to homepage 2021-07-14 08:40:04 -03:00
58 changed files with 2363 additions and 1387 deletions

View File

@ -77,7 +77,8 @@ local task_build(version, arch) = {
],
},
environment: {
DOCKERAUTH: { from_variable: 'dockerauth' },
DOCKERAUTH: { from_variable: 'harbor-auth' },
DOCKERURL: { from_variable: 'harbor-url' },
},
shell: '/busybox/sh',
working_dir: '/workspace',
@ -90,13 +91,13 @@ local task_build(version, arch) = {
cat << EOF > /kaniko/.docker/config.json
{
"auths": {
"https://index.docker.io/v1/": { "auth" : "$DOCKERAUTH" }
"$DOCKERURL": { "auth" : "$DOCKERAUTH" }
}
}
EOF
|||,
},
{ type: 'run', command: '/kaniko/executor --destination sorintlab/agola-web:$AGOLA_GIT_TAG' },
{ type: 'run', command: '/kaniko/executor --destination $DOCKERURL/tux/agola-web:$AGOLA_GIT_TAG' },
],
depends: ['checkout code and save to workspace'],
when: {

3
.prettierrc.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
singleQuote: true,
};

View File

@ -1,4 +1,4 @@
FROM node:lts-alpine AS web_build
FROM node:12-alpine AS web_build
WORKDIR /agola-web

View File

@ -1,5 +1,3 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
presets: ['@vue/cli-plugin-babel/preset'],
};

View File

@ -1,13 +1,9 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"*": [
"types/*"
]
}
},
"include": [
"./src/**/*"
]
}
"compilerOptions": {
"baseUrl": ".",
"paths": {
"*": ["types/*"]
}
},
"include": ["./src/**/*"]
}

49
package-lock.json generated
View File

@ -1977,6 +1977,13 @@
"yallist": "^2.1.2"
}
},
"prettier": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"dev": true,
"optional": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -4759,9 +4766,9 @@
"dev": true
},
"dns-packet": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
"integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
"integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
"dev": true,
"requires": {
"ip": "^1.1.0",
@ -9849,11 +9856,10 @@
"dev": true
},
"prettier": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"dev": true,
"optional": true
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"dev": true
},
"pretty-error": {
"version": "2.1.2",
@ -12652,10 +12658,11 @@
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.2.0",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.2.0.tgz",
"integrity": "sha512-TitGhqSQ61RJljMmhIGvfWzJ2zk9m1Qug049Ugml6QP3t0e95o0XJjk29roNEiPKJQBEi8Ord5hFuSuELzSp8Q==",
"version": "npm:vue-loader@16.1.2",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz",
"integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
@ -12667,15 +12674,17 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@ -12686,6 +12695,7 @@
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
@ -12694,25 +12704,29 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"dev": true,
"optional": true
},
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"dev": true
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@ -12724,6 +12738,7 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}

View File

@ -29,6 +29,7 @@
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^4.14.1",
"prettier": "2.5.1",
"sass-loader": "^8.0.2",
"tailwindcss": "^1.9.6",
"vue-template-compiler": "^2.6.12"

View File

@ -1,6 +1,3 @@
module.exports = {
"plugins": [
require('tailwindcss')('tailwind.js'),
require('autoprefixer')(),
]
}
plugins: [require('tailwindcss')('tailwind.js'), require('autoprefixer')()],
};

View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1940 1240.1947"
xml:space="preserve"
id="svg2"
version="1.1"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs6"><clipPath
id="clipPath18"
clipPathUnits="userSpaceOnUse"><path
id="path16"
d="M 0,930.146 H 1454.995 V 0 H 0 Z" /></clipPath></defs><g
transform="matrix(1.3333333,0,0,-1.3333333,0,1240.1947)"
id="g10"><g
id="g12"><g
clip-path="url(#clipPath18)"
id="g14"><g
transform="translate(183.6719,292.4514)"
id="g20"><path
id="path22"
style="fill:#23261f;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -5.566,1.016 -12.144,1.523 -17.202,1.523 -41.494,0 -70.84,-45.537 -70.84,-84.502 0,-24.794 12.651,-36.425 27.324,-36.425 16.699,0 40.479,14.668 52.114,78.427 z m -18.218,-185.186 c 1.016,14.678 2.027,29.346 2.53,44.024 h -1.011 c -22.261,-39.473 -55.152,-50.098 -83.487,-50.098 -46.547,0 -83.486,34.912 -83.486,92.598 0,85 57.681,168.486 191.768,168.486 33.393,0 69.824,-7.08 93.608,-14.668 L 75.391,-72.861 c -5.059,-27.315 -10.625,-77.92 -9.615,-112.325 z" /></g><g
transform="translate(496.377,290.9377)"
id="g24"><path
id="path26"
style="fill:#f37021;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -5.059,2.021 -15.181,3.037 -23.276,3.037 -38.453,0 -66.28,-35.42 -66.28,-77.422 0,-27.314 15.176,-37.939 31.368,-37.939 18.72,0 39.975,15.176 48.574,62.236 z m -172.031,-205.937 c 14.673,-8.096 52.114,-13.155 73.364,-13.155 33.398,0 60.718,11.133 69.824,57.178 l 2.027,10.117 h -1.011 c -19.229,-19.736 -38.457,-28.838 -67.803,-28.838 -53.633,0 -90.571,40.479 -90.571,96.133 0,80.957 59.707,155.84 180.132,155.84 39.97,0 71.845,-5.557 103.725,-17.705 L 61.226,-149.268 C 53.13,-192.783 38.96,-238.32 5.059,-263.613 c -29.849,-21.758 -68.814,-27.324 -103.218,-27.324 -39.468,0 -72.862,6.064 -93.101,16.699 z" /></g><g
transform="translate(716.4942,210.9963)"
id="g28"><path
id="path30"
style="fill:#f37021;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 0,-27.832 14.673,-41.494 36.934,-41.494 38.96,0 55.151,49.59 55.151,81.973 0,23.271 -10.117,42.5 -35.923,42.5 C 13.657,82.979 0,27.314 0,0 m 186.704,45.029 c 0,-100.693 -72.353,-154.834 -163.433,-154.834 -61.728,0 -117.89,33.907 -117.89,108.789 0,86.524 65.268,152.295 161.914,152.295 66.284,0 119.409,-39.97 119.409,-106.25" /></g><g
transform="translate(1267.0167,292.4514)"
id="g32"><path
id="path34"
style="fill:#23261f;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -5.566,1.016 -12.144,1.523 -17.202,1.523 -41.494,0 -70.84,-45.537 -70.84,-84.502 0,-24.794 12.651,-36.425 27.324,-36.425 16.695,0 40.479,14.668 52.114,78.427 z m -18.218,-185.186 c 1.016,14.678 2.027,29.346 2.534,44.024 h -1.015 c -22.261,-39.473 -55.152,-50.098 -83.487,-50.098 -46.547,0 -83.486,34.912 -83.486,92.598 0,85 57.681,168.486 191.768,168.486 33.393,0 69.824,-7.08 93.603,-14.668 L 75.391,-72.861 c -5.059,-27.315 -10.625,-77.92 -9.615,-112.325 z" /></g><g
transform="translate(1007.8321,485.4104)"
id="g36"><path
id="path38"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 0.19,0.01 -0.049,-0.049 Z" /></g><g
transform="translate(989.9092,606.8997)"
id="g40"><path
id="path42"
style="fill:#23261f;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C -120.383,-88.113 -319.265,-165.833 -401.267,-16.353 -299.682,-163.98 -112.424,-68.142 -18.081,-1.025 -6.823,19.741 17.4,55.164 40.047,82.378 18.616,70.146 4.612,65.154 -7.153,62.026 -40.51,34.409 -86.895,2.563 -136.719,-18.523 -207.5,-44.832 -279.499,-56.338 -337.634,-3.811 -244.57,-76.453 -109.912,-14.675 -26.325,57.705 -4.424,99.998 10.018,124.617 29.549,152.261 10.264,138.137 -6.042,129.307 -20.083,125.183 -76.719,61.135 -150.21,13.137 -232.268,8.337 c -68.095,-3.984 -99.204,54.339 -99.204,91.228 0,23.243 8.255,43.433 23.108,59.039 -10.991,-4.786 -20.93,-11.622 -32.414,-20.113 l -3.98,1.846 c -0.488,7.168 0.532,12.725 2.417,17.139 -1.806,-1.905 -3.457,-3.809 -5.01,-5.606 -3.169,-3.662 -5.908,-6.826 -8.891,-8.799 l -2.915,0.108 c -2.388,1.855 -3.74,4.258 -4.024,7.148 -0.464,4.658 2.012,9.844 5.205,14.58 -7.841,-5.469 -15.952,-12.129 -22.314,-17.607 l -4.131,1.914 c 0.049,7.91 5.386,19.433 14.697,30.166 -19.721,-12.109 -27.373,-20.166 -33.828,-26.953 -1.958,-2.071 -3.813,-4.024 -5.844,-5.928 l -4.18,1.406 c -1.782,10.635 8.052,25.742 16.582,36.162 -13.008,-8.232 -21.343,-18.134 -26.348,-30.996 l -4.8,0.508 c -2.866,17.891 4.996,41.104 14.19,56.631 -18.589,-15.615 -27.559,-41.24 -28.247,-53.75 l -4.59,-1.231 c -8.936,13.68 -7.734,34.454 -2.295,54.498 -86.121,-127.256 -26.094,-359.712 176.633,-359.712 91.819,0 178.68,41.882 250.693,88.994 C -7.067,-42.283 25.01,-6.182 47.781,17.515 23.862,4.661 10.815,1.519 0,0" /></g><g
transform="translate(1183.4816,907.4319)"
id="g44"><path
id="path46"
style="fill:#344026;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 10.742,-2.139 -9.8,-4.892 c -26.594,-13.303 -50.908,-40.786 -63.191,-69.214 19.109,17.314 39.422,32.187 64.891,45.464 33.962,17.705 119.355,30.21 178.532,3.613 -8.892,7.122 -18.718,12.478 -28.967,16.675 C 62.131,36.023 -5.61,16.27 -29.907,7.454 -35.762,5.33 -41.294,2.915 -46.685,0.364 -31.294,3.186 -15.654,3.101 0,0" /></g><g
transform="translate(1044.7511,785.3127)"
id="g48"><path
id="path50"
style="fill:#2c3324;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 -11.265,-15.283 c -22.846,-31.004 -37.514,-54.517 -59.37,-96.011 6.316,5.84 12.32,11.709 17.881,17.554 C -0.657,-39.004 31.24,1.909 64.38,34.453 73.425,65.771 98.132,98.33 126.724,116.367 104.87,118.74 83.093,114.741 62.119,104.824 14.924,71.05 -15.928,18.87 -61.299,-37.202 c -2.327,-2.876 -4.741,-5.679 -7.132,-8.499 14.984,5.994 32.601,17.095 53.597,33.855 z" /></g><g
transform="translate(1239.6876,847.8518)"
id="g52"><path
id="path54"
style="fill:#344026;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 12.124,-2.539 0.63,-7.148 c -19.185,-7.708 -45.747,-32.369 -65.816,-55.538 9.771,6.565 20.611,13.372 32.959,20.562 C 62.803,13.203 184.395,-70.83 215.308,-162.795 211.784,42.71 37.505,50.327 -40.052,13.247 -50.063,8.459 -59.432,3.32 -68.33,-2.122 -42.964,2.703 -13.563,2.837 0,0" /></g><g
transform="translate(1055.7618,710.8987)"
id="g56"><path
id="path58"
style="fill:#2c3324;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 -11.899,-13.184 C -33.69,-37.31 -59.487,-73.45 -74.163,-97.935 c 5.444,4.029 10.556,7.93 15.188,11.595 83.667,66.208 99.221,101.687 155.574,144.775 16.995,23.621 48.147,57.075 73.982,72.639 -21.219,1.336 -54.165,-1.504 -74.438,-9.219 C 46.812,86.462 11.193,40.356 -39.868,-11.863 -46.24,-18.379 -54.202,-25.84 -63.34,-33.782 c 11.875,4.143 26.716,10.991 48.267,24.397 z" /></g><g
transform="translate(1209.3556,785.9988)"
id="g60"><path
id="path62"
style="fill:#344026;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -15.853,-5.701 -30.916,-13.997 -45.428,-23.853 35.667,7.618 80.918,3.731 101.004,-15.7 l 4.82,-4.668 -6.495,-1.66 c -23.842,-6.104 -73.03,-35.66 -104.221,-60.215 179.168,60.813 361.748,-163.777 32.778,-275.713 273.064,14.668 288.47,219.917 219.446,319.136 C 150.84,10.732 61.823,22.234 0,0" /></g><g
transform="translate(1145.837,748.9065)"
id="g64"><path
id="path66"
style="fill:#2c3324;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -42.053,-33.196 -79.553,-77.856 -117.131,-110.996 -9.046,-7.976 -19.007,-16.074 -29.588,-24.136 11.287,2.793 26.387,8.753 51.82,23.931 l 18.339,10.947 -15.317,-14.892 c -17.767,-17.259 -51.006,-53.809 -71.685,-78.426 71.036,48.352 126.089,99.568 154.786,115.408 25.747,25.029 84.956,63.081 117.461,74.521 C 83.838,13.372 31.489,12.952 0,0" /></g><g
transform="translate(922.336,756.0208)"
id="g68"><path
id="path70"
style="fill:#f37021;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -0.944,3.213 -2.446,6.333 -4.963,7.632 -1.048,0.542 -2.832,1.396 -5.029,2.471 -8.653,4.238 -23.135,11.328 -27.905,14.228 l 1.298,2.129 c 4.673,-2.842 19.654,-10.176 27.705,-14.111 2.207,-1.084 4.006,-1.922 5.049,-2.481 8.062,-4.316 7.471,-14.895 7.93,-18.186 0.552,-3.87 0.869,-8.42 1.079,-13.882 28.781,28.97 9.24,69.143 -20.732,86.275 -5.992,-2.471 -26.058,-24.2 -35.43,-40.2 C -35.451,17.944 -19.395,10.659 -2.866,1.726 -2.146,1.338 -1.105,0.703 0,0" /></g><g
transform="translate(931.3506,781.8118)"
id="g72"><path
id="path74"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c 0,-3.041 -2.466,-5.507 -5.507,-5.507 -3.041,0 -5.507,2.466 -5.507,5.507 0,3.042 2.466,5.507 5.507,5.507 C -2.466,5.507 0,3.042 0,0" /></g><g
transform="translate(770.9083,907.6028)"
id="g76"><path
id="path78"
style="fill:#23261f;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -36.182,4.995 -72.246,3.169 -111.885,22.544 5.066,-14.751 5.674,-26.777 31.446,-32.749 -40.45,-1.69 -74.317,-13.486 -101.663,-32.192 -0.098,-0.181 -0.295,-0.44 -0.427,-0.65 -6.707,-4.634 -13.13,-9.565 -19.048,-14.985 -6.936,-6.353 -13.399,-13.15 -19.248,-20.445 -7.075,-19.008 -11.509,-41.513 -6.143,-57.251 3.721,16.895 15.952,44.493 40.908,57.1 l 2.964,-3.926 c -9.336,-10.088 -20.688,-34.082 -21.587,-54.15 7.608,13.35 19.629,23.35 37.818,31.699 l 2.617,-4.219 c -5.923,-4.785 -21.86,-23.632 -25,-36.738 0.356,0.371 0.713,0.742 1.074,1.123 8.745,9.209 19.629,20.664 57.788,40.723 l 2.466,-4.346 c -17.197,-10.508 -27.9,-25.986 -31.313,-36.65 9.311,7.744 20.722,16.455 30.009,21.162 l 3.033,-3.848 -1.05,-1.221 c -4.087,-4.736 -12.588,-14.58 -11.963,-20.859 0.083,-0.889 0.351,-1.66 0.815,-2.344 1.934,1.67 3.975,4.024 6.26,6.66 6.524,7.54 14.644,16.924 28.789,21.075 l 2.124,-4.463 c -1.138,-0.781 -2.358,-1.553 -3.623,-2.354 -7.29,-4.59 -15.459,-9.746 -16.011,-24.023 13.902,10.112 26.133,17.69 41.006,21.89 0.742,0.581 1.56,1.103 2.324,1.669 10.391,7.701 23.079,13.501 37.371,17.544 l -0.137,0.264 c -0.337,0.01 -0.705,-0.034 -1.045,-0.029 0.098,0.005 0.223,0.044 0.32,0.044 0.244,0 0.486,-0.005 0.725,-0.015 13.379,3.75 28.348,5.747 44.648,5.747 28.387,0 60.833,-6.059 96.068,-19.497 9.372,16.001 29.438,37.73 35.429,40.2 C 130.324,-45.493 53.034,-5.508 0,0" /></g><g
transform="translate(1070.84,479.7854)"
id="g80"><path
id="path82"
style="fill:#f37021;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 1.24,0.234 2.378,0.693 3.584,1.182 5.537,1.963 7.771,2.72 10.91,2.966 7.59,6.064 6.647,7.534 7.187,8.74 7.383,9.17 7.9,9.873 9.463,9.717 12.995,9.302 20.148,6.736 26.904,-0.073 24.57,3.474 21.392,7.012 18.112,9.497 14.224,12.446 6.081,18.574 1.157,18.784 -1.033,18.879 -3.523,16.868 -4.155,14.863 -5.423,10.85 -13.374,6.487 -20.433,3.445 -12.879,0.996 -2.386,-0.503 0,0" /></g><g
transform="translate(1114.5607,456.2746)"
id="g84"><path
id="path86"
style="fill:#f37021;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -0.027,0.186 -0.063,0.376 -0.107,0.568 -0.074,0.33 -0.169,0.662 -0.287,0.999 -0.114,0.337 -0.251,0.679 -0.41,1.026 -0.314,0.695 -0.705,1.408 -1.172,2.143 -0.929,1.47 -2.145,3.02 -3.598,4.639 -1.094,1.211 -2.322,2.466 -3.672,3.74 l -0.017,-0.014 c -0.151,0.168 -0.332,0.297 -0.488,0.461 -1.17,1.086 -2.407,2.185 -3.745,3.281 -7.461,5.535 -16.397,7.219 -17.661,7.244 -3.57,-0.039 -5.86,-0.84 -8.052,-1.719 -1.255,-0.508 -2.559,-1.025 -4.033,-1.318 -3.953,-0.735 -20.145,1.963 -27.256,5.903 -9.617,4.453 -18.823,11.582 -31.196,24.507 -16.675,16.97 -22.93,31.606 -26.234,43.403 -5.703,-3.955 -11.452,-7.832 -17.233,-11.604 l -0.047,-0.031 c 5.752,-10.123 18.968,-24.908 29.3,-33.79 l -1.631,-1.894 c -10.428,8.96 -23.74,23.896 -29.744,34.331 -2.788,-1.804 -5.583,-3.579 -8.381,-5.33 3.291,-4.514 7.175,-8.96 11.476,-13.203 11.654,-11.562 25.56,-22.072 37.354,-33.747 0.096,-0.093 0.188,-0.186 0.283,-0.279 0.008,-0.097 0.018,-0.195 0.025,-0.293 0.642,-8.244 -8.76,-7.041 -17.561,-6.215 -5.352,0.544 -11.048,1.071 -14.261,0.009 -4.767,-2.702 -7.126,-5.1 -7.36,-7.373 -0.122,-1.137 0.28,-2.251 1.144,-3.361 0.433,-0.555 0.98,-1.111 1.636,-1.67 0.33,-0.279 0.684,-0.559 1.065,-0.84 0.381,-0.281 0.789,-0.562 1.218,-0.845 5.671,-3.625 15.083,-7.535 23.435,-10.015 0.945,-0.217 2.01,0.185 3.047,0.779 -5.801,2.403 -12.046,5.987 -14.743,9.859 -1.172,1.689 -1.602,3.339 -1.284,4.912 l 2.451,-0.508 c -0.181,-0.869 0.122,-1.875 0.888,-2.969 2.525,-3.636 9.161,-7.224 14.973,-9.49 1.136,1.214 1.912,2.522 1.57,3.191 -1.494,3.052 -1.215,5.152 0.865,6.321 2.172,1.228 6.276,1.35 12.172,0.193 3.02,0.977 4.986,3.259 6.104,7.114 0.871,-0.281 1.74,-0.561 2.604,-0.844 -3.608,-12.886 1.649,-19.906 11.763,-24.94 2.078,-0.356 3.848,-0.529 5.57,-0.654 -1.844,3.828 -3.003,7.348 -1.851,9.539 0.391,0.742 1.226,1.66 2.988,1.826 5.681,0.645 11.314,-2.598 14.658,-7.632 0.787,0.591 1.555,1.221 2.266,1.936 0.371,3.928 -2.161,6.992 -6.245,10.027 -0.02,0.015 -0.005,0.034 -0.024,0.049 -0.376,0.22 -0.801,0.483 -1.167,0.698 -8.418,4.942 -15.064,8.848 -18.531,16.826 l 2.295,0.996 c 3.14,-7.236 9.478,-10.957 17.5,-15.664 0.564,-0.329 1.206,-0.727 1.789,-1.071 0.257,-0.01 0.498,0.026 0.762,-0.049 3.655,-1.051 7.659,-1.099 12.293,-0.281 2.048,1.938 4.072,2.549 6.203,1.943 2.103,-0.598 4.046,-2.285 5.862,-5.11 2.891,-2.808 5.611,-3.306 7.408,-3.278 3.916,0.099 6.989,2.599 6.955,5.849 -0.02,2.603 -1.936,5.42 -5.293,8.072 0.18,0.363 0.354,0.722 0.522,1.079 9.268,-5.706 14.642,-10.469 17.442,-14.564 1.899,-2.858 2.449,-5.378 1.399,-8.071 1.489,0.266 2.646,0.95 3.337,1.936 0.345,0.496 0.574,1.067 0.669,1.706 0.049,0.321 0.066,0.66 0.047,1.014 C 0.044,-0.369 0.026,-0.186 0,0" /></g><g
transform="translate(869.2452,786.0159)"
id="g88"><path
id="path90"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -33.083,12.197 -64.68,18.374 -93.975,18.374 -16.191,0 -31.422,-1.929 -45.287,-5.737 l -0.086,0.005 c -7.39,0 -15.993,-3.052 -25.954,-6.582 -1.36,-0.484 -2.801,-0.967 -4.202,-1.456 14.397,19.629 20.96,19.751 27.402,19.742 1.714,-0.039 3.492,-0.039 5.411,0.293 l 0.283,2.373 c -11.87,5.205 -23.316,-0.703 -34.38,-6.407 -2.9,-1.494 -5.669,-2.929 -8.389,-4.121 4.077,15.205 21.958,21.192 25.655,22.276 l -0.206,2.441 c -10.615,1.27 -24.301,-1.406 -37.426,-7.217 11.303,13.33 25.508,19.434 41.269,17.764 1.455,-0.205 2.93,-0.43 4.458,-0.693 l 0.464,2.451 c -1.465,0.303 -2.969,0.537 -4.58,0.723 -36.519,5.019 -51.807,-1.758 -70.43,-13.956 13.672,21.221 40.005,23.467 40.284,23.487 l -0.03,2.5 c -20.512,0.967 -36.65,-1.338 -51.982,-15.332 6.719,15.058 18.071,23.476 34.555,25.693 l -0.073,2.49 c -28.535,2.129 -51.279,-11.816 -59.59,-26.787 -1.679,4.824 -1.503,9.551 0.523,14.102 4.238,9.502 15.386,15.781 21.836,17.685 9.951,2.823 20.078,2.979 27.739,2.705 2.437,-0.166 4.893,-0.322 7.383,-0.459 l 0.161,2.5 c -2.051,0.147 -4.575,0.332 -7.441,0.45 -0.035,0.009 -0.069,0.009 -0.098,0.009 -23.694,1.534 -46.191,2.979 -72.993,-17.587 1.218,1.518 2.263,2.861 2.964,3.93 27.902,18.833 60.925,29.17 98.169,30.728 l 36.858,1.538 -35.94,8.33 c -16.631,3.852 -21.116,9.927 -24.307,18.696 26.775,-10.923 51.968,-13.232 76.426,-15.478 8.713,-0.801 17.721,-1.626 26.675,-2.857 C -46.501,111.177 23.665,72.925 32.007,36.509 23.47,30.186 8.623,13.35 0,0" /></g><g
transform="translate(839.5167,833.345)"
id="g92"><path
id="path94"
style="fill:#f37021;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -3.06,-6.934 -1.943,-14.6 2.16,-20.356 1.014,-0.616 1.953,-1.06 2.749,-1.177 6.151,-0.874 22.806,3.12 33.143,5.239 0.7,1.69 1.105,3.413 1.343,5.147 -8.326,8.051 -12.933,18.764 -19.307,21.718 -1.196,0.557 -3.037,0.982 -5.254,1.333 C 8.479,10.605 2.817,6.387 0,0" /></g><g
transform="translate(870.0899,821.116)"
id="g96"><path
id="path98"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -2.328,-5.278 -8.495,-7.669 -13.774,-5.341 -5.279,2.329 -7.67,8.496 -5.341,13.775 2.329,5.278 8.496,7.67 13.774,5.34 C -0.062,11.446 2.329,5.279 0,0" /></g><g
transform="translate(883.7061,818.179)"
id="g100"><path
id="path102"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C -1.751,1.206 -3.315,2.583 -4.795,4.019 -5.032,2.285 -5.437,0.562 -6.138,-1.128 -3.696,-0.625 -1.497,-0.195 0,0" /></g><g
transform="translate(839.5167,833.345)"
id="g104"><path
id="path106"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 0,0 C 2.817,6.387 8.479,10.605 14.834,11.904 2.715,13.813 -22.202,13.115 -33.896,14.771 -22.588,6.855 -6.538,-15.068 2.16,-20.356 -1.943,-14.6 -3.06,-6.934 0,0" /></g><g
transform="translate(1074.8829,462.2951)"
id="g108"><path
id="path110"
style="fill:#23261f;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -0.098,0.039 -2.373,-1.572 -3.96,-2.07 -1.587,-0.493 -3.828,-0.547 -5.615,-0.616 1.457,-2.797 1.299,-6.547 -0.357,-9.502 -1.655,-2.954 -6.562,-3.271 -6.562,-3.271 0,0 -9.004,7.471 -12.256,7.559 1.509,-3.301 2.148,-9.048 2.148,-9.048 0,0 -5.752,-3.023 -10.798,-1.621 -6.223,1.728 -16.418,11.274 -17.439,19.243 -0.391,-0.166 -0.796,-0.313 -1.216,-0.449 L -58.55,0.068 c -4.077,0.791 -6.391,0.782 -7.607,0.635 0.005,-0.019 0.014,-0.029 0.019,-0.039 0.918,-1.816 1.28,-4.873 -2.422,-8.818 l -3.432,-2.705 c -2.373,-1.368 -4.531,-1.846 -6.646,-1.319 -1.469,0.435 -2.927,0.948 -4.389,1.441 l -65.879,-344.292 h 90.063 L 7.773,-6.88 C 3.137,-4.424 0.603,-0.171 0,0" /></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,7 +1,9 @@
<template>
<div id="app">
<nav class="bg-gray-800 p-3 text-white">
<div class="container flex items-center justify-between flex-wrap bg-gray-800">
<div
class="container flex items-center justify-between flex-wrap bg-gray-800"
>
<div class="mr-6">
<router-link
class="font-semibold flex items-center flex-shrink-0 text-xl tracking-tight"
@ -28,12 +30,12 @@
</div>
<div
class="w-full block flex-grow lg:flex lg:items-center lg:w-auto"
:class="{'hidden' : !navActive}"
:class="{ hidden: !navActive }"
>
<div class="text-sm lg:flex-grow"></div>
<div v-if="user" class="relative mr-3">
<button
v-click-outside="() => createDropdownActive = false"
v-click-outside="() => (createDropdownActive = false)"
@click="createDropdownActive = !createDropdownActive"
class="relative flex items-center focus:outline-none"
>
@ -49,7 +51,8 @@
<router-link
class="block px-4 py-2 hover:bg-blue-500 hover:text-white"
to="/neworganization"
>New Organization</router-link>
>New Organization</router-link
>
</li>
</ul>
</div>
@ -57,11 +60,11 @@
<div v-if="user" class="relative">
<div class="flex">
<button
v-click-outside="() => userDropdownActive = false"
v-click-outside="() => (userDropdownActive = false)"
@click="userDropdownActive = !userDropdownActive"
class="relative flex items-center focus:outline-none"
>
{{user.username}}
{{ user.username }}
<i class="mdi mdi-chevron-down"></i>
</button>
</div>
@ -72,7 +75,7 @@
<ul>
<li class="block px-4 py-2 border-b">
Logged as&nbsp;
<b>{{user.username}}</b>
<b>{{ user.username }}</b>
</li>
<li>
<hr class="navbar-divider" />
@ -98,18 +101,30 @@
</div>
</div>
<div v-else class="navbar-item">
<router-link class="btn btn-blue" to="/register">Sign up</router-link>
<router-link class="ml-2 btn btn-blue" to="/login">Login</router-link>
<router-link class="btn btn-blue" to="/register"
>Sign up</router-link
>
<router-link class="ml-2 btn btn-blue" to="/login"
>Login</router-link
>
</div>
</div>
</div>
</nav>
<div v-if="error" class="container h-screen" role="alert">
<div v-if="error" class="h-full flex justify-center items-center" role="alert">
<div
v-if="error"
class="h-full flex justify-center items-center"
role="alert"
>
<div v-if="error" class="w-full" role="alert">
<div class="bg-red-500 text-white font-bold rounded-t px-4 py-2">Error</div>
<div class="border border-t-0 border-red-400 rounded-b bg-red-100 px-4 py-3 text-red-700">
<div class="bg-red-500 text-white font-bold rounded-t px-4 py-2">
Error
</div>
<div
class="border border-t-0 border-red-400 rounded-b bg-red-100 px-4 py-3 text-red-700"
>
<p class="mb-8">Failed to fetch data: {{ error }}</p>
<button class="btn btn-red" @click="reload()">Retry</button>
</div>
@ -123,48 +138,46 @@
</div>
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import { mapGetters } from "vuex";
import { mapGetters } from 'vuex';
import { ownerSettingsLink } from "@/util/link.js";
import { ownerSettingsLink } from '@/util/link.js';
export default {
name: "App",
name: 'App',
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
components: {},
computed: {
...mapGetters(["error", "user"])
...mapGetters(['error', 'user']),
},
data() {
return {
routerActive: true,
navActive: false,
userDropdownActive: false,
createDropdownActive: false
createDropdownActive: false,
};
},
watch: {
$route: function() {
$route: function () {
this.userDropdownActive = false;
this.createDropdownActive = false;
}
},
},
// method to reload current view from https://github.com/vuejs/vue-router/issues/296#issuecomment-356530037
methods: {
ownerSettingsLink: ownerSettingsLink,
reload() {
this.$store.dispatch("setError", null);
this.$store.dispatch('setError', null);
this.routerActive = false;
this.$nextTick(() => (this.routerActive = true));
}
}
},
},
};
</script>
<style lang="scss">
</style>
<style lang="scss"></style>

View File

@ -8,19 +8,21 @@
type="text"
placeholder="Organization name"
v-model="orgName"
>
/>
</div>
</div>
<div class="mb-4">
<label>
<input type="checkbox" v-model="orgIsPrivate">
<input type="checkbox" v-model="orgIsPrivate" />
Private
</label>
</div>
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
@click="createOrg()"
>Create Organization</button>
>
Create Organization
</button>
<div
v-if="createOrgError"
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
@ -32,19 +34,19 @@
</template>
<script>
import { createOrganization } from "@/util/data.js";
import { createOrganization } from '@/util/data.js';
import { ownerLink } from "@/util/link.js";
import { ownerLink } from '@/util/link.js';
export default {
components: {},
name: "createorganization",
name: 'createorganization',
props: {},
data() {
return {
createOrgError: null,
orgIsPrivate: false,
orgName: null
orgName: null,
};
},
methods: {
@ -54,9 +56,9 @@ export default {
async createOrg() {
this.resetErrors();
let visibility = "public";
let visibility = 'public';
if (this.orgIsPrivate) {
visibility = "private";
visibility = 'private';
}
let { error } = await createOrganization(this.orgName, visibility);
@ -65,14 +67,11 @@ export default {
return;
}
this.$router.push(ownerLink("org", this.orgName));
}
this.$router.push(ownerLink('org', this.orgName));
},
},
created: async function() {}
created: async function () {},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,17 +7,18 @@
type="text"
placeholder="Project Name"
v-model="projectName"
>
/>
<div class="mb-4">
<label>
<input type="checkbox" v-model="projectIsPrivate">
<input type="checkbox" v-model="projectIsPrivate" />
Private
</label>
</div>
<div class="mb-4">
<label class="checkbox">
<input type="checkbox" v-model="pass_vars_to_forked_pr" />
Pass variables to run even if triggered by PR from forked repo (DANGEROUS)
Pass variables to run even if triggered by PR from forked repo
(DANGEROUS)
</label>
</div>
<div class="mb-3 flex items-center">
@ -31,31 +32,48 @@
v-for="(rs, index) in remoteSources"
v-bind:key="rs.id"
:value="index"
>{{ rs.name }}</option>
>
{{ rs.name }}
</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"></path>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2"
>
<svg
class="fill-current h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<path
d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"
></path>
</svg>
</div>
</div>
<button
class="ml-3 btn btn-blue"
v-bind:class="{ 'spinner': fetchRemoteReposLoading }"
v-bind:class="{ spinner: fetchRemoteReposLoading }"
:disabled="selectedRemoteSourceIndex == null"
@click="fetchRemoteRepos()"
>Fetch remote repositories</button>
>
Fetch remote repositories
</button>
</div>
<div v-if="remoteRepos.length">
<h4 class="text-xl">Available remote repositories</h4>
<remoterepos :remoterepos="remoteRepos" v-on:reposelected="repoSelected($event)"/>
<remoterepos
:remoterepos="remoteRepos"
v-on:reposelected="repoSelected($event)"
/>
<button
class="btn btn-blue"
v-bind:class="{ 'spinner': createProjectLoading }"
v-bind:class="{ spinner: createProjectLoading }"
:disabled="!createProjectButtonEnabled"
@click="createProject()"
>Create Project</button>
>
Create Project
</button>
<div
v-if="createProjectError"
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
@ -72,20 +90,20 @@ import {
fetchCurrentUser,
fetchRemoteSources,
createProject,
userRemoteRepos
} from "@/util/data.js";
userRemoteRepos,
} from '@/util/data.js';
import { projectLink } from "@/util/link.js";
import { projectLink } from '@/util/link.js';
import remoterepos from "@/components/remoterepos.vue";
import remoterepos from '@/components/remoterepos.vue';
export default {
components: { remoterepos },
name: "createproject",
name: 'createproject',
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
@ -98,15 +116,15 @@ export default {
remoteSources: null,
remoteRepos: [],
selectedRemoteSourceIndex: null,
projectName: "",
projectName: '',
projectIsPrivate: false,
remoteRepoPath: null
remoteRepoPath: null,
};
},
computed: {
createProjectButtonEnabled: function() {
createProjectButtonEnabled: function () {
return this.projectName.length && this.remoteRepoPath;
}
},
},
watch: {},
methods: {
@ -143,7 +161,7 @@ export default {
let { data, error } = await userRemoteRepos(remoteSource.id);
this.stopFetchRemoteReposLoading();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.remoteRepos = data;
@ -155,11 +173,11 @@ export default {
if (this.projectgroupref) {
refArray = [...refArray, ...this.projectgroupref];
}
let parentref = refArray.join("/");
let parentref = refArray.join('/');
let visibility = "public";
let visibility = 'public';
if (this.projectIsPrivate) {
visibility = "private";
visibility = 'private';
}
let remoteSource = this.remoteSources[this.selectedRemoteSourceIndex];
@ -186,12 +204,12 @@ export default {
this.$router.push(
projectLink(this.ownertype, this.ownername, projectref)
);
}
},
},
created: async function() {
created: async function () {
let { data, error } = await fetchCurrentUser();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.user = data;
@ -199,7 +217,7 @@ export default {
// TODO(sgotti) filter only remote source where the user has a linked account
({ data, error } = await fetchRemoteSources());
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
@ -215,10 +233,8 @@ export default {
}
this.remoteSources = remoteSources;
}
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -5,9 +5,11 @@
<button
@click="clicked"
class="relative flex items-center focus:outline-none bg-green-500 hover:bg-green-600 text-white font-semibold hover:text-white py-2 px-4 border border-green-700 rounded rounded-r-none"
>{{ buttonValue }}</button>
>
{{ buttonValue }}
</button>
<button
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
class="relative flex items-center focus:outline-none bg-green-500 hover:bg-green-600 text-white font-semibold hover:text-white py-2 px-4 border border-l-0 border-green-700 rounded rounded-l-none"
>
@ -24,14 +26,16 @@
href="#"
class="block px-4 py-2 hover:bg-blue-500 hover:text-white"
@click="setButton('project')"
>New Project</a>
>New Project</a
>
</li>
<li>
<a
href="#"
class="block px-4 py-2 hover:bg-blue-500 hover:text-white"
@click="setButton('projectgroup')"
>New Project Group</a>
>New Project Group</a
>
</li>
</ul>
</div>
@ -40,28 +44,28 @@
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
export default {
components: {},
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
name: "createprojectbutton",
name: 'createprojectbutton',
props: {},
data() {
return {
dropdownActive: false,
type: "project"
type: 'project',
};
},
computed: {
buttonValue: function() {
if (this.type == "project") {
return "New Project";
buttonValue: function () {
if (this.type == 'project') {
return 'New Project';
}
return "New Project Group";
}
return 'New Project Group';
},
},
methods: {
setButton(type) {
@ -69,12 +73,10 @@ export default {
this.dropdownActive = false;
},
clicked() {
this.$emit("click", this.type);
}
}
this.$emit('click', this.type);
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,20 +7,22 @@
type="text"
placeholder="Project Group Name"
v-model="projectGroupName"
>
/>
<div class="mb-4">
<label>
<input type="checkbox" v-model="projectGroupIsPrivate">
<input type="checkbox" v-model="projectGroupIsPrivate" />
Private
</label>
</div>
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
v-bind:class="{ 'spinner': createProjectGroupLoading }"
v-bind:class="{ spinner: createProjectGroupLoading }"
:disabled="!createProjectGroupButtonEnabled"
@click="createProjectGroup()"
>Create ProjectGroup</button>
>
Create ProjectGroup
</button>
<div
v-if="createProjectGroupError"
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
@ -32,31 +34,31 @@
</template>
<script>
import { createProjectGroup } from "@/util/data.js";
import { createProjectGroup } from '@/util/data.js';
import { projectGroupLink } from "@/util/link.js";
import { projectGroupLink } from '@/util/link.js';
export default {
components: {},
name: "createprojectgroup",
name: 'createprojectgroup',
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
createProjectGroupError: null,
createProjectGroupLoading: false,
createProjectGroupLoadingTimeout: null,
projectGroupName: "",
projectGroupIsPrivate: false
projectGroupName: '',
projectGroupIsPrivate: false,
};
},
computed: {
createProjectGroupButtonEnabled: function() {
createProjectGroupButtonEnabled: function () {
return this.projectGroupName.length;
}
},
},
methods: {
resetErrors() {
@ -78,11 +80,11 @@ export default {
if (this.projectgroupref) {
refArray = [...refArray, ...this.projectgroupref];
}
let parentref = refArray.join("/");
let parentref = refArray.join('/');
let visibility = "public";
let visibility = 'public';
if (this.projectGroupIsPrivate) {
visibility = "private";
visibility = 'private';
}
this.startProjectGroupLoading();
@ -104,12 +106,9 @@ export default {
this.$router.push(
projectGroupLink(this.ownertype, this.ownername, projectgroupref)
);
}
}
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,178 @@
<template>
<div class="w-6/12">
<h1>
If you need more advanced options or a little help,
<a
class="text-blue-600"
rel="noopener noreferrer nofollow"
href="https://agola.io/tryit/#test-using-a-local-gitea-instance"
>
take a look at the documentation
</a>
</h1>
<form
class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"
@submit.prevent="
$emit('createSource', {
token,
name,
type,
clientId,
clientSecret,
url,
skipVerify,
sshHostKey,
skipSshHostKeyCheck,
})
"
>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="token"
>Agola Admin token</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="token"
type="password"
placeholder="Agola Admin token"
v-model="token"
/>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="type">Type</label>
<select
class="border bg-white rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="type"
v-model="type"
>
<option value="gitea">Gitea</option>
<option value="gitlab">GitLab</option>
<option value="github">GitHub</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="name"
>Source name</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="name"
pattern="^[a-zA-Z][a-zA-Z0-9]*([-]?[a-zA-Z0-9]+)+$"
type="text"
placeholder="Source name (only numbers, letters and -)"
v-model="name"
/>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="url"
>Source API URL</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="url"
type="text"
placeholder="API URL"
v-model="url"
/>
</div>
<div class="mb-4 flex flex-row">
<input
id="skip_verify"
type="checkbox"
class="h-6 w-6 border border-gray-300 rounded-md checked:bg-blue-600 checked:border-transparent focus:outline-none"
v-model="skipVerify"
/>
<label class="text-sm mt-auto font-bold mx-2" for="skip_verify"
>Skip TLS certificate validation</label
>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="ssh_key"
>Source Public SSH Key</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="ssh_key"
type="text"
autocomplete="off"
placeholder="Public SSH key"
v-model="sshHostKey"
/>
</div>
<div class="mb-4 flex flex-row">
<input
id="skip_ssh_key_check"
type="checkbox"
class="h-6 w-6 border border-gray-300 rounded-md checked:bg-blue-600 checked:border-transparent focus:outline-none"
v-model="skipSshHostKeyCheck"
/>
<label class="text-sm mt-auto font-bold mx-2" for="skip_ssh_key_check"
>Skip SSH host key check</label
>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="client_id"
>Source client ID</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="client_id"
type="text"
placeholder="Client ID"
v-model="clientId"
/>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="client_secret"
>Source client secret</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="client_secret"
type="password"
placeholder="Client secret"
v-model="clientSecret"
/>
</div>
<div class="flex justify-center">
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>
Create
</button>
</div>
</form>
</div>
</template>
<script>
import { GITHUB_SSH_KEY, GITHUB_API_URL } from '@/util/data';
export default {
name: 'CreateSourceForm',
data: function () {
return {
name: null,
token: null,
url: null,
sshHostKey: null,
skipVerify: false,
skipSshHostKeyCheck: false,
type: 'gitea', // default value for select
clientId: null,
clientSecret: null,
};
},
watch: {
type: function (value) {
if (value === 'github') {
this.url = GITHUB_API_URL;
this.sshHostKey = GITHUB_SSH_KEY;
this.skipVerify = false;
this.skipSshHostKeyCheck = false;
}
},
},
};
</script>

View File

@ -43,18 +43,18 @@
</template>
<script>
import { apiurl, fetch } from "@/util/auth";
import AnsiUp from "ansi_up";
import { apiurl, fetch } from '@/util/auth';
import AnsiUp from 'ansi_up';
export default {
name: "Log",
name: 'Log',
props: {
show: Boolean,
runid: String,
taskid: String,
setup: Boolean,
step: Number,
stepphase: String
stepphase: String,
},
computed: {},
data() {
@ -65,7 +65,7 @@ export default {
fetchAbort: null,
items: [],
lastitem: "",
lastitem: '',
lines: [],
formatter: formatter,
es: null,
@ -73,7 +73,7 @@ export default {
streaming: false,
done: false,
logExists: null,
error: null
error: null,
};
},
methods: {
@ -83,7 +83,7 @@ export default {
}
let follow = false;
if (this.stepphase == "running") {
if (this.stepphase == 'running') {
follow = true;
}
@ -94,14 +94,14 @@ export default {
this.logExists = null;
this.error = null;
let path = "/logs?runID=" + this.runid + "&taskID=" + this.taskid;
let path = '/logs?runID=' + this.runid + '&taskID=' + this.taskid;
if (this.setup) {
path += "&setup";
path += '&setup';
} else {
path += "&step=" + this.step;
path += '&step=' + this.step;
}
if (follow) {
path += "&follow";
path += '&follow';
}
try {
@ -111,7 +111,7 @@ export default {
this.streaming = true;
const reader = res.body.getReader();
let lastline = "";
let lastline = '';
let j = 0;
for (;;) {
let { done, value } = await reader.read();
@ -122,12 +122,12 @@ export default {
return;
}
let data = new TextDecoder("utf-8").decode(value, { stream: true });
let data = new TextDecoder('utf-8').decode(value, { stream: true });
let part = "";
let part = '';
for (var i = 0; i < data.length; i++) {
let c = data.charAt(i);
if (c == "\r") {
if (c == '\r') {
// replace lastline from start, simulating line feed (go to start of line)
// this isn't perfect since the previous line contents could have
// been written using different colors and this will lose them but
@ -136,17 +136,17 @@ export default {
lastline.slice(0, j) + part + lastline.slice(j + part.length);
j = 0;
this.lastitem = this.formatter.ansi_to_html(lastline);
part = "";
} else if (c == "\n") {
part = '';
} else if (c == '\n') {
lastline =
lastline.slice(0, j) + part + lastline.slice(j + part.length);
j += part.length;
this.lastitem = this.formatter.ansi_to_html(lastline);
this.items.push(this.lastitem);
this.lastitem = "";
lastline = "";
this.lastitem = '';
lastline = '';
j = 0;
part = "";
part = '';
} else {
part += c;
}
@ -174,10 +174,10 @@ export default {
this.fetchAbort.abort();
}
this.fetchAbort = new AbortController();
}
},
},
watch: {
show: function(post, pre) {
show: function (post, pre) {
if (pre == false && post == true) {
this.abortFetch();
this.fetch();
@ -186,23 +186,23 @@ export default {
this.abortFetch();
}
},
stepphase: function(post) {
stepphase: function (post) {
if (!this.show) {
return;
}
if (this.fetching) {
return;
}
if (post == "running") {
if (post == 'running') {
this.abortFetch();
this.getLogs(true);
} else {
this.abortFetch();
this.getLogs(false);
}
}
},
},
created: function() {
created: function () {
this.fetchAbort = new AbortController();
if (this.show) {
@ -217,6 +217,6 @@ export default {
if (this.es !== null) {
this.es.close();
}
}
},
};
</script>

View File

@ -5,50 +5,53 @@
@submit.prevent="$emit('login', { username, password })"
>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="username">Username</label>
<label class="block text-sm font-bold mb-2" for="username"
>Username</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="username"
type="text"
placeholder="Username"
v-model="username"
>
/>
</div>
<div class="mb-6">
<label class="block text-sm font-bold mb-2" for="password">Password</label>
<label class="block text-sm font-bold mb-2" for="password"
>Password</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 mb-3 leading-tight focus:outline-none focus:shadow-outline"
id="password"
type="password"
placeholder="******************"
v-model="password"
>
/>
</div>
<div class="flex justify-center">
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>{{action}} with {{name}}</button>
>
{{ action }} with {{ name }}
</button>
</div>
</form>
</div>
</template>
<script>
export default {
name: "LoginForm",
name: 'LoginForm',
props: {
action: String,
name: String
name: String,
},
data: function() {
data: function () {
return {
username: null,
password: null
password: null,
};
}
},
};
</script>

View File

@ -3,8 +3,8 @@
<h4 class="mb-3 text-xl">Organization Members</h4>
<ul v-if="members.length">
<li class="flex" v-for="member in members" v-bind:key="member.user.id">
<span class="w-1/2 font-bold">{{member.user.username}}</span>
<span class="w-1/2">{{member.role}}</span>
<span class="w-1/2 font-bold">{{ member.user.username }}</span>
<span class="w-1/2">{{ member.role }}</span>
</li>
</ul>
<div v-else>No Members</div>
@ -12,39 +12,38 @@
</template>
<script>
import { fetchOrgMembers } from "@/util/data.js";
import { fetchOrgMembers } from '@/util/data.js';
export default {
components: {},
name: "orgmembers",
name: 'orgmembers',
props: {
orgname: String
orgname: String,
},
data() {
return {
members: []
members: [],
};
},
watch: {
$route: async function() {
$route: async function () {
this.fetchOrgMembers(this.orgname);
}
},
},
methods: {
async fetchOrgMembers(orgname) {
let { data, error } = await fetchOrgMembers(orgname);
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.members = data.members;
}
},
},
created: function() {
created: function () {
this.fetchOrgMembers(this.orgname);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -2,57 +2,67 @@
<nav class="mb-4 bg-grey-light rounded font-sans w-full">
<ol class="list-reset flex text-grey-dark">
<li>
<a>{{ownertype}}</a>
<a>{{ ownertype }}</a>
</li>
<li>
<span class="mx-2">/</span>
</li>
<li>
<router-link :to="ownerLink(ownertype, ownername)">{{ownername}}</router-link>
<router-link :to="ownerLink(ownertype, ownername)">{{
ownername
}}</router-link>
</li>
<li v-for="(ref, i) in projectref" v-bind:key="i">
<span class="mx-2">/</span>
<router-link
v-if="i+1 < projectref.length"
:to="projectGroupLink(ownertype, ownername, projectref.slice(0, i+1))"
>{{ref}}</router-link>
v-if="i + 1 < projectref.length"
:to="
projectGroupLink(ownertype, ownername, projectref.slice(0, i + 1))
"
>{{ ref }}</router-link
>
<router-link
v-else
:to="projectLink(ownertype, ownername, projectref.slice(0, i+1))"
>{{ref}}</router-link>
:to="projectLink(ownertype, ownername, projectref.slice(0, i + 1))"
>{{ ref }}</router-link
>
</li>
<li v-for="(ref, i) in projectgroupref" v-bind:key="i">
<span class="mx-2">/</span>
<router-link
:to="projectGroupLink(ownertype, ownername, projectgroupref.slice(0, i+1))"
>{{ref}}</router-link>
:to="
projectGroupLink(
ownertype,
ownername,
projectgroupref.slice(0, i + 1)
)
"
>{{ ref }}</router-link
>
</li>
</ol>
</nav>
</template>
<script>
import { ownerLink, projectLink, projectGroupLink } from "@/util/link.js";
import { ownerLink, projectLink, projectGroupLink } from '@/util/link.js';
export default {
name: "projbreadcrumbs",
name: 'projbreadcrumbs',
components: {},
props: {
ownertype: String,
ownername: String,
projectref: Array,
projectgroupref: Array
projectgroupref: Array,
},
methods: {
ownerLink: ownerLink,
projectLink: projectLink,
projectGroupLink: projectGroupLink
}
projectGroupLink: projectGroupLink,
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -20,7 +20,9 @@
Private
</label>
</div>
<button class="btn btn-blue" @click="updateProjectGroup()">Update</button>
<button class="btn btn-blue" @click="updateProjectGroup()">
Update
</button>
<div
v-if="updateProjectGroupError"
@ -35,14 +37,22 @@
<div class="panel">
<p class="panel-title">Secrets</p>
<div class="p-4">
<projectsecrets :secrets="secrets" :allsecrets="allsecrets" type="projectgroup" />
<projectsecrets
:secrets="secrets"
:allsecrets="allsecrets"
type="projectgroup"
/>
</div>
</div>
<div class="panel">
<p class="panel-title">Variables</p>
<div class="p-4">
<projectvars :variables="variables" :allvariables="allvariables" type="projectgroup" />
<projectvars
:variables="variables"
:allvariables="allvariables"
type="projectgroup"
/>
</div>
</div>
@ -56,16 +66,13 @@
>
<p>
This operation
<strong>CANNOT</strong> be undone.
This operation will remove
<strong>{{projectGroupPath}}</strong>
<strong>CANNOT</strong> be undone. This operation will remove
<strong>{{ projectGroupPath }}</strong>
</p>
</div>
<label class="block mb-2">
Please type the project group name for confirmation:
<span
class="text-red-500 font-bold"
>{{ projectGroupName }}</span>
<span class="text-red-500 font-bold">{{ projectGroupName }}</span>
</label>
<div class="mb-4">
<input
@ -79,7 +86,9 @@
class="btn btn-red"
@click="deleteProjectGroup()"
:disabled="!deleteButtonEnabled"
>Delete Project Group</button>
>
Delete Project Group
</button>
</div>
</div>
<div
@ -98,21 +107,21 @@ import {
fetchSecrets,
fetchVariables,
updateProjectGroup,
deleteProjectGroup
} from "@/util/data.js";
deleteProjectGroup,
} from '@/util/data.js';
import { projectGroupLink } from "@/util/link.js";
import { projectGroupLink } from '@/util/link.js';
import projectsecrets from "@/components/projectsecrets";
import projectvars from "@/components/projectvars";
import projectsecrets from '@/components/projectsecrets';
import projectvars from '@/components/projectvars';
export default {
components: { projectsecrets, projectvars },
name: "projectgroupsettings",
name: 'projectgroupsettings',
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
@ -124,24 +133,24 @@ export default {
allsecrets: [],
variables: [],
allvariables: [],
projectGroupNameToDelete: ""
projectGroupNameToDelete: '',
};
},
computed: {
projectGroupName: function() {
projectGroupName: function () {
return this.projectgroupref[this.projectgroupref.length - 1];
},
projectGroupPath: function() {
return ["", this.ownertype, this.ownername, ...this.projectgroupref].join(
"/"
projectGroupPath: function () {
return ['', this.ownertype, this.ownername, ...this.projectgroupref].join(
'/'
);
},
deleteButtonEnabled: function() {
deleteButtonEnabled: function () {
return this.projectGroupNameToDelete == this.projectGroupName;
},
isRootProjectGroup() {
return this.projectgroupref.length == 0;
}
},
},
methods: {
resetErrors() {
@ -154,12 +163,12 @@ export default {
let projectgroupref = [
this.ownertype,
this.ownername,
...this.projectgroupref
].join("/");
...this.projectgroupref,
].join('/');
let visibility = "public";
let visibility = 'public';
if (this.projectGroupIsPrivate) {
visibility = "private";
visibility = 'private';
}
let { error } = await updateProjectGroup(
projectgroupref,
@ -175,8 +184,8 @@ export default {
let projectgroupref = [
this.ownertype,
this.ownername,
...this.projectgroupref
].join("/");
...this.projectgroupref,
].join('/');
if (this.projectGroupNameToDelete == this.projectGroupName) {
let { error } = await deleteProjectGroup(projectgroupref);
@ -192,70 +201,68 @@ export default {
)
);
}
}
},
},
created: async function() {
created: async function () {
let projectgroupref = [
this.ownertype,
this.ownername,
...this.projectgroupref
].join("/");
...this.projectgroupref,
].join('/');
let { data, error } = await fetchProjectGroup(projectgroupref);
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.projectGroup = data;
this.projectGroupIsPrivate = this.projectGroup.visibility == "private";
this.projectGroupIsPrivate = this.projectGroup.visibility == 'private';
({ data, error } = await fetchSecrets(
"projectgroup",
'projectgroup',
projectgroupref,
false
));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.secrets = data;
({ data, error } = await fetchSecrets(
"projectgroup",
'projectgroup',
projectgroupref,
true
));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.allsecrets = data;
({ data, error } = await fetchVariables(
"projectgroup",
'projectgroup',
projectgroupref,
false
));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.variables = data;
({ data, error } = await fetchVariables(
"projectgroup",
'projectgroup',
projectgroupref,
true
));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.allvariables = data;
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -2,13 +2,20 @@
<div>
<h4 class="text-xl my-3">Projects</h4>
<div v-if="fetchProjectsLoading" class="ml-6 flex w-48">
<div v-bind:class="{ 'spinner': fetchProjectsLoading }"></div>
<div v-bind:class="{ spinner: fetchProjectsLoading }"></div>
</div>
<ul v-else-if="projects.length > 0">
<li class="mb-2 border rounded-l" v-for="project in projects" v-bind:key="project.id">
<li
class="mb-2 border rounded-l"
v-for="project in projects"
v-bind:key="project.id"
>
<div class="pl-4 py-4 flex items-center">
<router-link class="item" :to="projectLink(ownertype, ownername, ref(project.name))">
<span class="font-bold">{{project.name}}</span>
<router-link
class="item"
:to="projectLink(ownertype, ownername, ref(project.name))"
>
<span class="font-bold">{{ project.name }}</span>
</router-link>
</div>
</li>
@ -19,7 +26,7 @@
<h4 class="text-xl my-3">Project Groups</h4>
<div v-if="fetchProjectGroupsLoading" class="ml-6 flex w-48">
<div v-bind:class="{ 'spinner': fetchProjectGroupsLoading }"></div>
<div v-bind:class="{ spinner: fetchProjectGroupsLoading }"></div>
</div>
<ul v-else-if="projectgroups.length > 0">
<li
@ -32,7 +39,7 @@
class="item"
:to="projectGroupLink(ownertype, ownername, ref(projectgroup.name))"
>
<span class="font-bold">{{projectgroup.name}}</span>
<span class="font-bold">{{ projectgroup.name }}</span>
</router-link>
</div>
</li>
@ -44,18 +51,18 @@
<script>
import {
fetchProjectGroupProjects,
fetchProjectGroupSubgroups
} from "@/util/data.js";
fetchProjectGroupSubgroups,
} from '@/util/data.js';
import { projectLink, projectGroupLink } from "@/util/link.js";
import { projectLink, projectGroupLink } from '@/util/link.js';
export default {
components: {},
name: "Projects",
name: 'Projects',
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
@ -65,18 +72,18 @@ export default {
fetchProjectsLoading: false,
projects: [],
projectgroups: []
projectgroups: [],
};
},
watch: {
$route: async function() {
$route: async function () {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
this.fetchAbort = new AbortController();
this.fetchProjects(this.ownertype, this.ownername);
this.fetchProjectGroups(this.ownertype, this.ownername);
}
},
},
methods: {
startFetchProjectsLoading() {
@ -107,7 +114,7 @@ export default {
this.startFetchProjectsLoading();
let { data, error, aborted } = await fetchProjectGroupProjects(
projectgroupref.join("/"),
projectgroupref.join('/'),
this.fetchAbort.signal
);
this.stopFetchProjectsLoading();
@ -115,7 +122,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.projects = data;
@ -127,7 +134,7 @@ export default {
}
this.startFetchProjectGroupsLoading();
let { data, error, aborted } = await fetchProjectGroupSubgroups(
projectgroupref.join("/"),
projectgroupref.join('/'),
this.fetchAbort.signal
);
this.stopFetchProjectGroupsLoading();
@ -135,15 +142,15 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.projectgroups = data;
},
projectLink: projectLink,
projectGroupLink: projectGroupLink
projectGroupLink: projectGroupLink,
},
created: function() {
created: function () {
this.fetchAbort = new AbortController();
this.fetchProjects(this.ownertype, this.ownername);
this.fetchProjectGroups(this.ownertype, this.ownername);
@ -152,9 +159,8 @@ export default {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,33 +7,34 @@
<hr class="my-6 border-t" />
<h5 class="text-2xl">All secrets (local and inherited)</h5>
<secrets v-if="allsecrets.length" :secrets="allsecrets" :showparentpath="true" />
<secrets
v-if="allsecrets.length"
:secrets="allsecrets"
:showparentpath="true"
/>
<span v-else>No secrets</span>
</div>
</template>
<script>
import secrets from "@/components/secrets";
import secrets from '@/components/secrets';
export default {
components: { secrets },
name: "projectsecrets",
name: 'projectsecrets',
props: {
secrets: Array,
allsecrets: Array,
type: String
type: String,
},
computed: {
typetitle() {
if (this.type == "project") return "Project";
if (this.type == "projectgroup") return "Project group";
return "";
}
}
if (this.type == 'project') return 'Project';
if (this.type == 'projectgroup') return 'Project group';
return '';
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -21,7 +21,8 @@
<div class="mb-4">
<label class="checkbox">
<input type="checkbox" v-model="project.pass_vars_to_forked_pr" />
Pass variables to run even if triggered by PR from forked repo (DANGEROUS)
Pass variables to run even if triggered by PR from forked repo
(DANGEROUS)
</label>
</div>
<button class="btn btn-blue" @click="updateProject()">Update</button>
@ -38,14 +39,22 @@
<div class="panel">
<p class="panel-title">Secrets</p>
<div class="p-4">
<projectsecrets :secrets="secrets" :allsecrets="allsecrets" type="project" />
<projectsecrets
:secrets="secrets"
:allsecrets="allsecrets"
type="project"
/>
</div>
</div>
<div class="panel">
<p class="panel-title">Variables</p>
<div class="p-4">
<projectvars :variables="variables" :allvariables="allvariables" type="project" />
<projectvars
:variables="variables"
:allvariables="allvariables"
type="project"
/>
</div>
</div>
@ -59,16 +68,13 @@
>
<p>
This operation
<strong>CANNOT</strong> be undone.
This operation will remove
<strong>{{projectPath}}</strong>
<strong>CANNOT</strong> be undone. This operation will remove
<strong>{{ projectPath }}</strong>
</p>
</div>
<label class="block mb-2">
Please type the project name for confirmation:
<span
class="text-red-500 font-bold"
>{{ projectName }}</span>
<span class="text-red-500 font-bold">{{ projectName }}</span>
</label>
<div class="mb-4">
<input
@ -82,7 +88,9 @@
class="btn btn-red"
@click="deleteProject()"
:disabled="!deleteButtonEnabled"
>Delete Project</button>
>
Delete Project
</button>
<div
v-if="deleteProjectError"
@ -93,21 +101,30 @@
</div>
</div>
<div class="p-4 border-t">
<h4 class="mb-4 title text-xl">Change remote repository linked account</h4>
<h4 class="mb-4 title text-xl">
Change remote repository linked account
</h4>
<div
class="mb-4 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded"
role="alert"
>
<p>This operation will change the linked account associated with the project remote repository to the current user linked account</p>
<p>
This operation will change the linked account associated with the
project remote repository to the current user linked account
</p>
</div>
<button class="btn btn-red" @click="updateRepoLinkedAccount()">Change</button>
<button class="btn btn-red" @click="updateRepoLinkedAccount()">
Change
</button>
<div
v-if="updateRepoLinkedAccountError"
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
role="alert"
>
<span class="block sm:inline">{{ updateRepoLinkedAccountError }}</span>
<span class="block sm:inline">{{
updateRepoLinkedAccountError
}}</span>
</div>
</div>
</div>
@ -121,21 +138,21 @@ import {
fetchVariables,
updateProject,
deleteProject,
projectUpdateRepoLinkedAccount
} from "@/util/data.js";
projectUpdateRepoLinkedAccount,
} from '@/util/data.js';
import { projectGroupLink } from "@/util/link.js";
import { projectGroupLink } from '@/util/link.js';
import projectsecrets from "@/components/projectsecrets";
import projectvars from "@/components/projectvars";
import projectsecrets from '@/components/projectsecrets';
import projectvars from '@/components/projectvars';
export default {
components: { projectsecrets, projectvars },
name: "projectsettings",
name: 'projectsettings',
props: {
ownertype: String,
ownername: String,
projectref: Array
projectref: Array,
},
data() {
return {
@ -148,19 +165,19 @@ export default {
allsecrets: [],
variables: [],
allvariables: [],
projectNameToDelete: ""
projectNameToDelete: '',
};
},
computed: {
projectName: function() {
projectName: function () {
return this.projectref[this.projectref.length - 1];
},
projectPath: function() {
return ["", this.ownertype, this.ownername, ...this.projectref].join("/");
projectPath: function () {
return ['', this.ownertype, this.ownername, ...this.projectref].join('/');
},
deleteButtonEnabled: function() {
deleteButtonEnabled: function () {
return this.projectNameToDelete == this.projectName;
}
},
},
methods: {
resetErrors() {
@ -174,12 +191,12 @@ export default {
let projectref = [
this.ownertype,
this.ownername,
...this.projectref
].join("/");
...this.projectref,
].join('/');
let visibility = "public";
let visibility = 'public';
if (this.projectIsPrivate) {
visibility = "private";
visibility = 'private';
}
let { error } = await updateProject(
projectref,
@ -198,8 +215,8 @@ export default {
let projectref = [
this.ownertype,
this.ownername,
...this.projectref
].join("/");
...this.projectref,
].join('/');
if (this.projectNameToDelete == this.projectName) {
let { error } = await deleteProject(projectref);
@ -222,60 +239,59 @@ export default {
let projectref = [
this.ownertype,
this.ownername,
...this.projectref
].join("/");
...this.projectref,
].join('/');
let { error } = await projectUpdateRepoLinkedAccount(projectref);
if (error) {
this.updateRepoLinkedAccountError = error;
return;
}
}
},
},
created: async function() {
created: async function () {
let projectref = [this.ownertype, this.ownername, ...this.projectref].join(
"/"
'/'
);
let { data, error } = await fetchProject(projectref);
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.project = data;
this.projectIsPrivate = this.project.visibility == "private";
this.projectIsPrivate = this.project.visibility == 'private';
({ data, error } = await fetchSecrets("project", projectref, false));
({ data, error } = await fetchSecrets('project', projectref, false));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.secrets = data;
({ data, error } = await fetchSecrets("project", projectref, true));
({ data, error } = await fetchSecrets('project', projectref, true));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.allsecrets = data;
({ data, error } = await fetchVariables("project", projectref, false));
({ data, error } = await fetchVariables('project', projectref, false));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.variables = data;
({ data, error } = await fetchVariables("project", projectref, true));
({ data, error } = await fetchVariables('project', projectref, true));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.allvariables = data;
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,32 +7,34 @@
<hr class="my-6 border-t" />
<h5 class="text-2xl">All variables (local and inherited)</h5>
<vars v-if="allvariables.length" :variables="allvariables" :showparentpath="true" />
<vars
v-if="allvariables.length"
:variables="allvariables"
:showparentpath="true"
/>
<span v-else>No variables</span>
</div>
</template>
<script>
import vars from "@/components/vars";
import vars from '@/components/vars';
export default {
components: { vars },
name: "projectvars",
name: 'projectvars',
props: {
variables: Array,
allvariables: Array,
type: String
type: String,
},
computed: {
typetitle() {
if (this.type == "project") return "Project";
if (this.type == "projectgroup") return "Project group";
return "";
}
}
if (this.type == 'project') return 'Project';
if (this.type == 'projectgroup') return 'Project group';
return '';
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -5,7 +5,9 @@
@submit.prevent="$emit('login', { username })"
>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="username">Remote Username</label>
<label class="block text-sm font-bold mb-2" for="username"
>Remote Username</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="username"
@ -13,38 +15,38 @@
placeholder="Username"
:disabled="true"
v-model="remoteUsername"
>
/>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="username">Username</label>
<label class="block text-sm font-bold mb-2" for="username"
>Username</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="username"
type="text"
placeholder="Username"
v-model="username"
>
/>
</div>
<div class="flex justify-center">
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>Register</button>
>
Register
</button>
</div>
</form>
</div>
</template>
<script>
export default {
name: "RegisterForm",
name: 'RegisterForm',
props: {
remoteUsername: String,
username: String
}
username: String,
},
};
</script>

View File

@ -7,8 +7,8 @@
v-bind:key="repo.id"
@click="select(index)"
>
<input type="radio" :checked="selectedrepo == index">
{{repo.path}}
<input type="radio" :checked="selectedrepo == index" />
{{ repo.path }}
</label>
</div>
<div v-else class="block px-4 py-2 border-b">No remote repositories</div>
@ -18,23 +18,22 @@
<script>
export default {
components: {},
name: "remoterepos",
name: 'remoterepos',
props: {
remoterepos: Array
remoterepos: Array,
},
data() {
return {
selectedrepo: null
selectedrepo: null,
};
},
methods: {
select(index) {
this.selectedrepo = index;
this.$emit("reposelected", this.remoterepos[index].path);
}
}
this.$emit('reposelected', this.remoterepos[index].path);
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -27,21 +27,29 @@
<div class="p-4 border border-l-0 rounded-r flex">
<div class="w-4/6 items-start justify-between">
<div class="flex items-center mb-1">
<h2 class="text-2xl mr-3">{{run.name}}</h2>
<h2 class="text-2xl mr-3">{{ run.name }}</h2>
<span
class="mr-3 rounded px-2 py-1 text-xs"
:class="'is-' + runResultClass(run)"
>{{ runStatus(run) | capitalize }}</span>
>{{ runStatus(run) | capitalize }}</span
>
<span
v-if="stillRunning(run)"
class="rounded bg-gray-500 text-white px-2 py-1 text-xs"
>Still running</span>
>Still running</span
>
</div>
<div class="mb-6">
{{ run.annotations.message.split(/\r?\n/)[0] }}
</div>
<div class="mb-6">{{run.annotations.message.split(/\r?\n/)[0]}}</div>
<div>
<a :href="run.annotations.commit_link" class="block" target="_blank">
<a
:href="run.annotations.commit_link"
class="block"
target="_blank"
>
<i class="mdi mdi-source-commit mdi-rotate-90"></i>
<span>{{run.annotations.commit_sha.substring(0,8)}}</span>
<span>{{ run.annotations.commit_sha.substring(0, 8) }}</span>
</a>
<a
v-if="run.annotations.ref_type == 'branch'"
@ -50,7 +58,7 @@
target="_blank"
>
<i class="mdi mdi-source-branch"></i>
<span>{{run.annotations.branch}}</span>
<span>{{ run.annotations.branch }}</span>
</a>
<a
v-else-if="run.annotations.ref_type == 'tag'"
@ -59,7 +67,7 @@
target="_blank"
>
<i class="mdi mdi-tag"></i>
<span>{{run.annotations.tag}}</span>
<span>{{ run.annotations.tag }}</span>
</a>
<a
v-else-if="run.annotations.ref_type == 'pull_request'"
@ -68,7 +76,7 @@
target="_blank"
>
<i class="mdi mdi-source-pull"></i>
<span>PR #{{run.annotations.pull_request_id}}</span>
<span>PR #{{ run.annotations.pull_request_id }}</span>
</a>
</div>
</div>
@ -85,12 +93,18 @@
<div class="w-1/6 flex items-start justify-between">
<div class="relative ml-auto mr-3">
<div
v-if="run.can_restart_from_scratch || run.can_restart_from_failed_tasks"
v-if="
run.can_restart_from_scratch ||
run.can_restart_from_failed_tasks
"
class="flex"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
>
<div class="flex items-center">
<button class="btn btn-blue" @click="dropdownActive = !dropdownActive">
<button
class="btn btn-blue"
@click="dropdownActive = !dropdownActive"
>
<span>Restart</span>
<i class="ml-3 mdi mdi-restart" aria-hidden="true"></i>
</button>
@ -107,14 +121,16 @@
v-if="run.can_restart_from_scratch"
class="block px-4 py-2 hover:bg-blue-500 hover:text-white cursor-pointer"
@click="restartRun(run.id, true)"
>From start</a>
>From start</a
>
</li>
<li>
<a
v-if="run.can_restart_from_failed_tasks"
class="block px-4 py-2 hover:bg-blue-500 hover:text-white cursor-pointer"
@click="restartRun(run.id)"
>From failed tasks</a>
>From failed tasks</a
>
</li>
</ul>
</div>
@ -122,13 +138,17 @@
class="btn btn-red"
v-if="run.phase == 'queued'"
@click="cancelRun(run.id)"
>Cancel</button>
>
Cancel
</button>
<button
class="btn btn-red"
v-if="run.phase == 'running'"
:disabled="run.stopping"
@click="stopRun(run.id)"
>Stop</button>
>
Stop
</button>
</div>
</div>
</div>
@ -138,27 +158,27 @@
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import { cancelRun, stopRun, restartRun } from "@/util/data.js";
import { userDirectRunLink, projectRunLink } from "@/util/link.js";
import { runStatus, runResultClass } from "@/util/run.js";
import { cancelRun, stopRun, restartRun } from '@/util/data.js';
import { userDirectRunLink, projectRunLink } from '@/util/link.js';
import { runStatus, runResultClass } from '@/util/run.js';
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
momentDurationFormatSetup(moment);
export default {
name: "rundetail",
name: 'rundetail',
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
ownertype: String,
ownername: String,
projectref: Array,
run: Object
run: Object,
},
data() {
return {
@ -166,7 +186,7 @@ export default {
stopRunError: null,
cancelRunError: null,
restartRunError: null,
dropdownActive: false
dropdownActive: false,
};
},
methods: {
@ -178,14 +198,14 @@ export default {
this.restartRunError = null;
},
stillRunning(run) {
return run.result != "unknown" && run.phase == "running";
return run.result != 'unknown' && run.phase == 'running';
},
taskClass(task) {
if (task.status == "success") return "success";
if (task.status == "failed") return "failed";
if (task.status == "stopped") return "failed";
if (task.status == "running") return "running";
return "unknown";
if (task.status == 'success') return 'success';
if (task.status == 'failed') return 'failed';
if (task.status == 'stopped') return 'failed';
if (task.status == 'running') return 'running';
return 'unknown';
},
async stopRun(runid) {
this.resetErrors();
@ -207,7 +227,7 @@ export default {
return;
}
this.run.phase = "cancelled";
this.run.phase = 'cancelled';
},
async restartRun(runid, fromStart) {
this.dropdownActive = false;
@ -231,7 +251,7 @@ export default {
this.$router.push(runLink);
},
duration(run) {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(run.start_time);
let end = moment(run.end_time);
@ -244,30 +264,29 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
endTime(run) {
let formatString = "lll";
let formatString = 'lll';
let end = moment(run.end_time);
if (run.end_time === null) {
return "";
return '';
}
return "Finished " + end.format(formatString);
return 'Finished ' + end.format(formatString);
},
endTimeHuman(run) {
let end = moment(run.end_time);
if (run.end_time === null) {
return "";
return '';
}
return end.fromNow();
}
},
},
created: function() {
created: function () {
window.setInterval(() => {
this.now = moment();
}, 500);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -9,7 +9,7 @@
</div>
<div class="ml-6 flex w-48">
<div v-bind:class="{ 'spinner': fetchRunsLoading }"></div>
<div v-bind:class="{ spinner: fetchRunsLoading }"></div>
</div>
<div v-if="runs">
<ul>
@ -27,21 +27,21 @@
class="whitespace-no-wrap overflow-x-hidden"
>
<i class="mdi mdi-source-branch mr-1"></i>
<span>{{run.annotations.branch}}</span>
<span>{{ run.annotations.branch }}</span>
</div>
<div
v-else-if="run.annotations.ref_type == 'tag'"
class="whitespace-no-wrap overflow-x-hidden"
>
<i class="mdi mdi-tag mr-1"></i>
<span>{{run.annotations.tag}}</span>
<span>{{ run.annotations.tag }}</span>
</div>
<div
v-else-if="run.annotations.ref_type == 'pull_request'"
class="whitespace-no-wrap overflow-x-hidden"
>
<i class="mdi mdi-source-pull mr-1"></i>
<span>PR #{{run.annotations.pull_request_id}}</span>
<span>PR #{{ run.annotations.pull_request_id }}</span>
</div>
</div>
<div v-else class="w-2/12">
@ -53,30 +53,36 @@
class="w-5/12 pl-3 mr-auto whitespace-no-wrap overflow-hidden"
:to="projectRunLink(ownertype, ownername, projectref, run.id)"
>
<span class="font-bold">{{run.name}}</span>
<div>{{run.annotations.message.split(/\r?\n/)[0]}}</div>
<span class="font-bold">{{ run.name }}</span>
<div>{{ run.annotations.message.split(/\r?\n/)[0] }}</div>
</router-link>
<router-link
v-else
class="w-5/12 pl-3 mr-auto whitespace-no-wrap overflow-hidden"
:to="userDirectRunLink(ownername, run.id)"
>
<span class="font-bold">{{run.name}}</span>
<div>{{run.annotations.message.split(/\r?\n/)[0]}}</div>
<span class="font-bold">{{ run.name }}</span>
<div>{{ run.annotations.message.split(/\r?\n/)[0] }}</div>
</router-link>
<span
v-if="waitingApproval(run)"
class="w-2/12 bg-gray-200 rounded-full px-3 py-1 text-sm text-center font-semibold mr-2"
>Waiting Approval</span>
>Waiting Approval</span
>
<span
v-if="stillRunning(run)"
class="w-2/12 bg-gray-200 rounded-full px-3 py-1 text-sm text-center font-semibold mr-2"
>Still running</span>
>Still running</span
>
<div class="w-32">
<span>#{{run.counter}}</span>
<a :href="run.annotations.commit_link" class="block" target="_blank">
<span>#{{ run.counter }}</span>
<a
:href="run.annotations.commit_link"
class="block"
target="_blank"
>
<i class="mdi mdi-source-commit mdi-rotate-90 mr-1"></i>
<span>{{run.annotations.commit_sha.substring(0,8)}}</span>
<span>{{ run.annotations.commit_sha.substring(0, 8) }}</span>
</a>
</div>
<div class="w-32">
@ -97,7 +103,9 @@
v-if="hasMoreRuns"
class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded"
@click="loadMoreRuns()"
>Load more...</button>
>
Load more...
</button>
</div>
</div>
<div v-if="runs && runs.length == 0" class>No runs</div>
@ -105,22 +113,22 @@
</template>
<script>
import { fetchUser, fetchProject, fetchRuns } from "@/util/data.js";
import { userDirectRunLink, projectRunLink } from "@/util/link.js";
import { runResultClass } from "@/util/run.js";
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import { fetchUser, fetchProject, fetchRuns } from '@/util/data.js';
import { userDirectRunLink, projectRunLink } from '@/util/link.js';
import { runResultClass } from '@/util/run.js';
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
momentDurationFormatSetup(moment);
export default {
components: {},
name: "runs",
name: 'runs',
props: {
ownertype: String,
ownername: String,
projectref: Array,
query: String
query: String,
},
data() {
return {
@ -137,15 +145,15 @@ export default {
wantedRunsNumber: 25,
hasMoreRuns: false,
project: null,
user: null
user: null,
};
},
watch: {
$route: function() {
$route: function () {
this.runs = null;
this.hasMoreRuns = false;
this.update();
}
},
},
methods: {
projectRunLink: projectRunLink,
@ -161,7 +169,7 @@ export default {
this.fetchRunsLoading = false;
},
stillRunning(run) {
return run.result != "unknown" && run.phase == "running";
return run.result != 'unknown' && run.phase == 'running';
},
waitingApproval(run) {
return run.tasks_waiting_approval.length > 0;
@ -183,8 +191,8 @@ export default {
let projectref = [
this.ownertype,
this.ownername,
...this.projectref
].join("/");
...this.projectref,
].join('/');
let { data, error, aborted } = await fetchProject(
projectref,
@ -194,7 +202,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.project = data;
@ -212,7 +220,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.user = data;
@ -228,17 +236,17 @@ export default {
let group;
let lastrun = false;
if (this.project !== null) {
if (this.query == "branches") {
group = "/project/" + this.project.id + "/branch";
} else if (this.query == "tags") {
group = "/project/" + this.project.id + "/tag";
} else if (this.query == "pullrequests") {
group = "/project/" + this.project.id + "/pr";
if (this.query == 'branches') {
group = '/project/' + this.project.id + '/branch';
} else if (this.query == 'tags') {
group = '/project/' + this.project.id + '/tag';
} else if (this.query == 'pullrequests') {
group = '/project/' + this.project.id + '/pr';
} else {
group = "/project/" + this.project.id;
group = '/project/' + this.project.id;
}
} else if (this.user !== null) {
group = "/user/" + this.user.id;
group = '/user/' + this.user.id;
}
let newRuns = [];
@ -289,7 +297,7 @@ export default {
}, 2000);
},
duration(run) {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(run.start_time);
let end = moment(run.end_time);
@ -302,24 +310,24 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
endTime(run) {
let formatString = "lll";
let formatString = 'lll';
let end = moment(run.end_time);
if (run.end_time === null) {
return "";
return '';
}
return "Finished " + end.format(formatString);
return 'Finished ' + end.format(formatString);
},
endTimeHuman(run) {
let end = moment(run.end_time);
if (run.end_time === null) {
return "";
return '';
}
return end.fromNow();
}
},
},
created: function() {
created: function () {
window.setInterval(() => {
this.now = moment();
}, 500);
@ -331,9 +339,8 @@ export default {
this.fetchAbort.abort();
}
clearTimeout(this.fetchRunsSchedule);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,7 +7,12 @@
>
<div>{{ fetchRunError }}</div>
</div>
<rundetail :run="run" :ownertype="ownertype" :ownername="ownername" :projectref="projectref" />
<rundetail
:run="run"
:ownertype="ownertype"
:ownername="ownername"
:projectref="projectref"
/>
<div v-if="run">
<div v-if="run.phase != 'setuperror'">
<div class="flex items-center my-6 justify-between">
@ -17,7 +22,7 @@
<button
@click="tasksDisplay = 'graph'"
class="relative flex items-center focus:outline-none bg-blue-500 hover:bg-blue-600 text-white font-semibold hover:text-white py-2 px-4 border border-blue-700 rounded rounded-r-none"
:class="{ 'bg-blue-600': tasksDisplay=='graph'}"
:class="{ 'bg-blue-600': tasksDisplay == 'graph' }"
title="Tasks Graph"
>
<i class="mr-1 mdi mdi-file-tree" />
@ -26,7 +31,7 @@
@click="tasksDisplay = 'list'"
class="relative flex items-center focus:outline-none bg-blue-500 hover:bg-blue-600 text-white font-semibold hover:text-white py-2 px-4 border border-l-0 border-blue-700 rounded rounded-l-none"
title="Tasks List"
:class="{ 'bg-blue-600': tasksDisplay=='list'}"
:class="{ 'bg-blue-600': tasksDisplay == 'list' }"
>
<i class="mr-1 mdi mdi-format-list-bulleted-square" />
</button>
@ -42,7 +47,8 @@
class="font-mono leading-snug text-xs"
v-for="(error, i) in run.setup_errors"
v-bind:key="i"
>{{error}}</pre>
>{{ error }}</pre
>
</div>
</div>
</div>
@ -50,21 +56,21 @@
</template>
<script>
import { fetchRun } from "@/util/data.js";
import { userDirectRunTaskLink, projectRunTaskLink } from "@/util/link.js";
import { fetchRun } from '@/util/data.js';
import { userDirectRunTaskLink, projectRunTaskLink } from '@/util/link.js';
import rundetail from "@/components/rundetail.vue";
import tasks from "@/components/tasks.vue";
import tasksgraph from "@/components/tasksgraph.vue";
import rundetail from '@/components/rundetail.vue';
import tasks from '@/components/tasks.vue';
import tasksgraph from '@/components/tasksgraph.vue';
export default {
name: "runsummary",
name: 'runsummary',
components: { rundetail, tasks, tasksgraph },
props: {
ownertype: String,
ownername: String,
projectref: Array,
runid: String
runid: String,
},
data() {
return {
@ -80,11 +86,11 @@ export default {
taskYSpace: 20,
hoverTask: null,
tasksDisplay: "graph"
tasksDisplay: 'graph',
};
},
watch: {
$route: async function() {
$route: async function () {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
@ -93,7 +99,7 @@ export default {
this.fetchAbort = new AbortController();
this.fetchRun();
}
},
},
methods: {
runTaskLink(task) {
@ -110,16 +116,16 @@ export default {
}
},
parents(task) {
return Object.keys(task.depends).map(key => {
return Object.keys(task.depends).map((key) => {
return this.run.tasks[task.depends[key].task_id].name;
});
},
taskClass(task) {
if (task.status == "success") return "success";
if (task.status == "failed") return "failed";
if (task.status == "stopped") return "failed";
if (task.status == "running") return "running";
return "unknown";
if (task.status == 'success') return 'success';
if (task.status == 'failed') return 'failed';
if (task.status == 'stopped') return 'failed';
if (task.status == 'running') return 'running';
return 'unknown';
},
async fetchRun() {
let { data, error, aborted } = await fetchRun(
@ -145,9 +151,8 @@ export default {
let task = tasks[taskID];
task.link = this.runTaskLink(task);
task.parents = this.parents(task);
task.waiting_approval = this.run.tasks_waiting_approval.includes(
taskID
);
task.waiting_approval =
this.run.tasks_waiting_approval.includes(taskID);
}
this.scheduleFetchRun();
@ -157,9 +162,9 @@ export default {
this.fetchRunSchedule = setTimeout(() => {
this.fetchRun();
}, 2000);
}
},
},
created: function() {
created: function () {
this.fetchAbort = new AbortController();
this.fetchRun();
},
@ -168,9 +173,8 @@ export default {
this.fetchAbort.abort();
}
clearTimeout(this.fetchRunSchedule);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -5,8 +5,10 @@
</div>
<div class="flex" v-for="secret in secrets" v-bind:key="secret.id">
<div class="w-2/12">
<span class="name">{{secret.name}}</span>
<div v-if="showparentpath" class="text-sm font-light">from {{secret.parent_path}}</div>
<span class="name">{{ secret.name }}</span>
<div v-if="showparentpath" class="text-sm font-light">
from {{ secret.parent_path }}
</div>
</div>
</div>
</div>
@ -15,16 +17,12 @@
<script>
export default {
components: {},
name: "secrets",
name: 'secrets',
props: {
secrets: Array,
showparentpath: Boolean
}
showparentpath: Boolean,
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -12,7 +12,7 @@
class="inline-block mr-1 mdi mdi-arrow-right"
:class="{ 'arrow-down': active, 'arrow-right': !active }"
></i>
<span class="w-1/3 font-bold">{{step.name}}</span>
<span class="w-1/3 font-bold">{{ step.name }}</span>
</div>
<span class>{{ duration }}</span>
</div>
@ -20,18 +20,24 @@
<div v-if="step.type == 'run'">
<div class="p-3 rounded-t bg-gray-900 text-white">
<span>
<span class="w-2/12 bg-gray-700 rounded-l px-3 py-1 text-center font-semibold">Shell</span>
<span
class="w-2/12 bg-gray-700 rounded-l px-3 py-1 text-center font-semibold"
>Shell</span
>
<span
class="w-2/12 bg-gray-600 rounded-r px-3 py-1 text-center font-semibold mr-2"
>{{ step.shell}}</span>
>{{ step.shell }}</span
>
</span>
<span v-if="step.exit_status != undefined">
<span
class="w-2/12 bg-gray-700 rounded-l px-3 py-1 text-center font-semibold"
>Exit Status</span>
>Exit Status</span
>
<span
class="w-2/12 bg-gray-600 rounded-r px-3 py-1 text-center font-semibold mr-2"
>{{ step.exit_status}}</span>
>{{ step.exit_status }}</span
>
</span>
</div>
<div
@ -40,22 +46,27 @@
>
<i
class="inline-block mr-1 mdi mdi-arrow-right"
:class="{ 'arrow-down': commandActive, 'arrow-right': !commandActive }"
:class="{
'arrow-down': commandActive,
'arrow-right': !commandActive,
}"
></i>
<span>Command</span>
</div>
<div
v-show="commandActive"
class="p-3 bg-gray-900 text-white overflow-x-auto"
:class="{ 'rounded': step.type != 'run' }"
:class="{ rounded: step.type != 'run' }"
>
<pre class="font-mono text-xs">{{ step.command}}</pre>
<pre class="font-mono text-xs">{{ step.command }}</pre>
</div>
</div>
<div v-if="step.type == 'run'" class="px-3 py-2 bg-gray-700 text-white">Log</div>
<div v-if="step.type == 'run'" class="px-3 py-2 bg-gray-700 text-white">
Log
</div>
<div
class="p-3 rounded-b bg-gray-900 text-white"
:class="{ 'rounded': step.type != 'run' }"
:class="{ rounded: step.type != 'run' }"
>
<Log
v-bind:runid="runid"
@ -72,22 +83,22 @@
</template>
<script>
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import Log from "@/components/log.vue";
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
import Log from '@/components/log.vue';
momentDurationFormatSetup(moment);
export default {
name: "step",
name: 'step',
components: {
Log
Log,
},
data() {
return {
active: false,
commandActive: true,
now: moment()
now: moment(),
};
},
props: {
@ -95,11 +106,11 @@ export default {
taskid: String,
setup: Boolean,
stepnum: Number,
step: Object
step: Object,
},
computed: {
duration() {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(this.step.start_time);
let end = moment(this.step.end_time);
@ -112,12 +123,12 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
stepClass() {
if (this.step.phase == "success") return "success";
if (this.step.phase == "failed") return "failed";
if (this.step.phase == "stopped") return "failed";
if (this.step.phase == "running") return "running";
return "unknown";
}
if (this.step.phase == 'success') return 'success';
if (this.step.phase == 'failed') return 'failed';
if (this.step.phase == 'stopped') return 'failed';
if (this.step.phase == 'running') return 'running';
return 'unknown';
},
},
methods: {
toggle() {
@ -125,13 +136,13 @@ export default {
},
toggleCommand() {
this.commandActive = !this.commandActive;
}
},
},
created() {
window.setInterval(() => {
this.now = moment();
}, 500);
}
},
};
</script>
@ -144,4 +155,4 @@ export default {
transition: transform 0.2s ease-in-out;
transform: rotate(90deg);
}
</style>
</style>

View File

@ -1,14 +1,18 @@
<template>
<div class="arrow">
<svg viewBox="0 0 15 15">
<path fill="none" stroke="#9d9d9d" d="M4.32.5l6.247 6.942L4.32 14.5"></path>
<path
fill="none"
stroke="#9d9d9d"
d="M4.32.5l6.247 6.942L4.32 14.5"
></path>
</svg>
</div>
</template>
<script>
export default {
name: "tabarrow"
name: 'tabarrow',
};
</script>
@ -20,4 +24,4 @@ export default {
position: relative;
top: 0.1rem;
}
</style>
</style>

View File

@ -2,24 +2,31 @@
<ul>
<li v-for="task in sortedTasks" v-bind:key="task.id">
<div class="mb-2 border-l-5 rounded-l" :class="taskClass(task)">
<div class="px-4 py-4 flex justify-between items-center border border-l-0 rounded-r">
<div
class="px-4 py-4 flex justify-between items-center border border-l-0 rounded-r"
>
<router-link class="w-1/3 font-bold" tag="a" :to="task.link">
<span class="w-1/3 font-bold">{{task.name}}</span>
<span class="w-1/3 font-bold">{{ task.name }}</span>
</router-link>
<div class="w-1/4">
<span
v-if="task.waiting_approval"
class="w-2/12 bg-gray-200 rounded-full px-3 py-1 text-sm text-center font-semibold mr-2"
>Waiting Approval</span>
>Waiting Approval</span
>
</div>
<div class="w-1/4">
<span class="block text-xs" v-if="task.parents.length > 0">depends on: &nbsp;</span>
<span class="block text-xs" v-if="task.parents.length > 0"
>depends on: &nbsp;</span
>
<ul>
<li
class="font-thin text-xs text-gray-600"
v-for="dep in task.parents"
v-bind:key="dep"
>{{dep}}</li>
>
{{ dep }}
</li>
</ul>
</div>
<span class="w-16 text-right">{{ task.duration }}</span>
@ -30,21 +37,21 @@
</template>
<script>
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
momentDurationFormatSetup(moment);
export default {
name: "tasks",
name: 'tasks',
components: {},
data() {
return {
now: moment()
now: moment(),
};
},
props: {
tasks: Object
tasks: Object,
},
computed: {
sortedTasks() {
@ -59,18 +66,18 @@ export default {
? -1
: 0
)
.map(k => tasks[k]);
.map((k) => tasks[k]);
for (let task of sortedTasks) {
task.duration = this.duration(task);
}
return sortedTasks;
}
},
},
methods: {
duration(task) {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(task.start_time);
let end = moment(task.end_time);
@ -83,18 +90,18 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
taskClass(task) {
if (task.status == "success") return "success";
if (task.status == "failed") return "failed";
if (task.status == "stopped") return "failed";
if (task.status == "running") return "running";
if (task.status == "skipped") return "skipped";
return "unknown";
}
if (task.status == 'success') return 'success';
if (task.status == 'failed') return 'failed';
if (task.status == 'stopped') return 'failed';
if (task.status == 'running') return 'running';
if (task.status == 'skipped') return 'skipped';
return 'unknown';
},
},
created() {
window.setInterval(() => {
this.now = moment();
}, 500);
}
},
};
</script>
</script>

View File

@ -64,13 +64,13 @@
</template>
<script>
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
momentDurationFormatSetup(moment);
export default {
name: "tasksgraph",
name: 'tasksgraph',
components: {},
data() {
return {
@ -85,14 +85,14 @@ export default {
taskYSpace: 20,
hoverTask: null,
height: "400px"
height: '400px',
};
},
props: {
tasks: Object
tasks: Object,
},
computed: {
segments: function() {
segments: function () {
let segments = [];
for (let edge of this.edges) {
for (let i = 0; i < edge.edgePoints.length - 1; i++) {
@ -107,7 +107,7 @@ export default {
}
// TODO(sgotti) set different colors to edges based on source task status???
let stroke = "text-dark";
let stroke = 'text-dark';
segments.push({
edge: edge,
x1: edge.edgePoints[i].x,
@ -115,7 +115,7 @@ export default {
x2: edge.edgePoints[i + 1].x,
y2: edge.edgePoints[i + 1].y,
strokeWidth: strokeWidth,
stroke: stroke
stroke: stroke,
});
}
}
@ -130,16 +130,16 @@ export default {
}
return this.graphTasks;
}
},
},
watch: {
tasks: function(tasks) {
tasks: function (tasks) {
this.update(tasks);
}
},
},
methods: {
duration(task) {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(task.start_time);
let end = moment(task.end_time);
@ -152,12 +152,12 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
taskClass(task) {
if (task.status == "success") return "success";
if (task.status == "failed") return "failed";
if (task.status == "stopped") return "failed";
if (task.status == "running") return "running";
if (task.status == "skipped") return "skipped";
return "unknown";
if (task.status == 'success') return 'success';
if (task.status == 'failed') return 'failed';
if (task.status == 'stopped') return 'failed';
if (task.status == 'running') return 'running';
if (task.status == 'skipped') return 'skipped';
return 'unknown';
},
update(tasks) {
// sort tasks by level
@ -169,7 +169,7 @@ export default {
? -1
: 0
)
.map(k => tasks[k]);
.map((k) => tasks[k]);
this.graphTasks = graphTasks;
@ -180,7 +180,7 @@ export default {
}
}
let taskChilds = function(tasks, task) {
let taskChilds = function (tasks, task) {
let childs = [];
for (let ot of tasks) {
for (let depTaskID in ot.depends) {
@ -192,7 +192,7 @@ export default {
return childs;
};
let taskMaxChildLevel = function(tasks, task) {
let taskMaxChildLevel = function (tasks, task) {
let level = task.level;
let childs = taskChilds(tasks, task);
for (let child of childs) {
@ -203,7 +203,7 @@ export default {
return level;
};
let levelTasks = function(tasks, level) {
let levelTasks = function (tasks, level) {
let levelTasks = [];
for (let task of tasks) {
if (task.level != level) {
@ -214,7 +214,7 @@ export default {
return levelTasks;
};
let levelsTasks = function(tasks, startLevel) {
let levelsTasks = function (tasks, startLevel) {
let levelTasks = [];
for (let task of tasks) {
if (task.level < startLevel) {
@ -225,13 +225,13 @@ export default {
return levelTasks;
};
let levelsTasksByRow = function(tasks, startLevel) {
let levelsTasksByRow = function (tasks, startLevel) {
return levelsTasks(tasks, startLevel).sort((a, b) =>
a.row > b.row ? 1 : b.row > a.row ? -1 : 0
);
};
let levelFreeRow = function(tasks, level) {
let levelFreeRow = function (tasks, level) {
let rows = [];
for (let task of tasks) {
if (task.level != level) {
@ -255,7 +255,7 @@ export default {
return prevrow;
};
let levelsMaxRow = function(tasks, level) {
let levelsMaxRow = function (tasks, level) {
let row = 0;
for (let task of tasks) {
if (level >= 0 && task.level > level) {
@ -344,7 +344,7 @@ export default {
sourceTask: pTask,
targetTask: curTask,
source: { level: pTask.level, row: pTask.row },
target: { level: curTask.level, row: curTask.row }
target: { level: curTask.level, row: curTask.row },
});
}
}
@ -362,30 +362,30 @@ export default {
edge.edgePoints.push({
x: (taskWidth + taskXSpace) * edge.source.level + taskWidth,
y: (taskHeight + taskYSpace) * edge.source.row + taskHeight / 2
y: (taskHeight + taskYSpace) * edge.source.row + taskHeight / 2,
});
edge.edgePoints.push({
x: (taskWidth + taskXSpace) * edge.target.level - taskXSpace / 2,
y: (taskHeight + taskYSpace) * edge.source.row + taskHeight / 2
y: (taskHeight + taskYSpace) * edge.source.row + taskHeight / 2,
});
edge.edgePoints.push({
x: (taskWidth + taskXSpace) * edge.target.level - taskXSpace / 2,
y: (taskHeight + taskYSpace) * edge.target.row + taskHeight / 2
y: (taskHeight + taskYSpace) * edge.target.row + taskHeight / 2,
});
edge.edgePoints.push({
x: (taskWidth + taskXSpace) * edge.target.level,
y: (taskHeight + taskYSpace) * edge.target.row + taskHeight / 2
y: (taskHeight + taskYSpace) * edge.target.row + taskHeight / 2,
});
}
let width = (maxlevel + 1) * (this.taskWidth + this.taskXSpace);
this.width = width + "px";
this.width = width + 'px';
let height =
(levelsMaxRow(graphTasks, -1) + 1) *
(this.taskHeight + this.taskYSpace);
this.height = height + "px";
}
this.height = height + 'px';
},
},
created() {
this.update(this.tasks);
@ -393,6 +393,6 @@ export default {
window.setInterval(() => {
this.now = moment();
}, 500);
}
},
};
</script>

View File

@ -8,22 +8,30 @@
<div>Error fetching Run: {{ fetchRunError }}</div>
<div>Error fetching Task: {{ fetchTaskError }}</div>
</div>
<rundetail :run="run" :ownertype="ownertype" :ownername="ownername" :projectref="projectref" />
<rundetail
:run="run"
:ownertype="ownertype"
:ownername="ownername"
:projectref="projectref"
/>
<div v-if="task != null">
<div class="mt-8 mb-4 flex justify-between items-center">
<div class="flex items-center">
<span class="text-2xl mr-3">{{task.name}}</span>
<span class="text-2xl mr-3">{{ task.name }}</span>
<span
class="mr-3 rounded px-2 py-1 text-xs"
:class="taskClass(task)"
>{{ task.status | capitalize }}</span>
>{{ task.status | capitalize }}</span
>
</div>
<button
v-if="task.waiting_approval"
class="btn btn-blue"
@click="approveTask(run.id, task.id)"
>Approve</button>
>
Approve
</button>
</div>
<step
v-bind:runid="runid"
@ -44,23 +52,23 @@
</template>
<script>
import { fetchRun, fetchTask, approveTask } from "@/util/data.js";
import { fetchRun, fetchTask, approveTask } from '@/util/data.js';
import step from "@/components/step.vue";
import rundetail from "@/components/rundetail.vue";
import step from '@/components/step.vue';
import rundetail from '@/components/rundetail.vue';
export default {
components: {
step,
rundetail
rundetail,
},
name: "tasksummary",
name: 'tasksummary',
props: {
ownertype: String,
ownername: String,
projectref: Array,
runid: String,
taskid: String
taskid: String,
},
data() {
return {
@ -70,11 +78,11 @@ export default {
fetchTaskError: null,
run: null,
task: null
task: null,
};
},
watch: {
$route: async function() {
$route: async function () {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
@ -85,16 +93,16 @@ export default {
this.fetchRun();
this.fetchTask();
}
},
},
methods: {
taskClass(task) {
if (task.status == "success") return "is-success";
if (task.status == "failed") return "is-failed";
if (task.status == "stopped") return "is-failed";
if (task.status == "running") return "is-running";
if (task.status == "skipped") return "is-skipped";
return "unknown";
if (task.status == 'success') return 'is-success';
if (task.status == 'failed') return 'is-failed';
if (task.status == 'stopped') return 'is-failed';
if (task.status == 'running') return 'is-running';
if (task.status == 'skipped') return 'is-skipped';
return 'unknown';
},
async fetchRun() {
let { data, error, aborted } = await fetchRun(
@ -143,9 +151,9 @@ export default {
this.fetchTask();
}, 2000);
},
approveTask: approveTask
approveTask: approveTask,
},
created: function() {
created: function () {
this.fetchAbort = new AbortController();
this.fetchRun();
@ -157,9 +165,8 @@ export default {
}
clearTimeout(this.fetchRunSchedule);
clearTimeout(this.fetchTaskSchedule);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,7 +7,10 @@
class="mb-4 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded"
role="alert"
>
<p>Removing a Linked Account will also block all the projects that uses this Linked Account to access their remote repository</p>
<p>
Removing a Linked Account will also block all the projects that uses
this Linked Account to access their remote repository
</p>
</div>
<ul v-if="user.linked_accounts">
<li
@ -16,10 +19,14 @@
v-bind:key="index"
>
<div>
<span class="font-bold">{{la.remote_user_name}}</span>
<span class="ml-1">(on remote source {{laRemoteSourceName(la)}})</span>
<span class="font-bold">{{ la.remote_user_name }}</span>
<span class="ml-1"
>(on remote source {{ laRemoteSourceName(la) }})</span
>
</div>
<button class="btn btn-red" @click="deleteLinkedAccount(la)">Delete</button>
<button class="btn btn-red" @click="deleteLinkedAccount(la)">
Delete
</button>
</li>
<div
v-if="deleteLinkedAccountError"
@ -40,9 +47,13 @@
class="block appearance-none w-full bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
v-model="selectedRemoteSourceName"
>
<option v-for="rs in remotesources" v-bind:key="rs.id">{{ rs.name }}</option>
<option v-for="rs in remotesources" v-bind:key="rs.id">
{{ rs.name }}
</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2">
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2"
>
<svg
class="fill-current h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
@ -56,7 +67,9 @@
</div>
</div>
<button class="ml-3 btn btn-blue" @click="addLinkedAccount()">Add Linked Account</button>
<button class="ml-3 btn btn-blue" @click="addLinkedAccount()">
Add Linked Account
</button>
</div>
</div>
@ -69,8 +82,10 @@
v-for="token in user.tokens"
v-bind:key="token"
>
<span class="font-bold">{{token}}</span>
<button class="btn btn-red" @click="deleteUserToken(token)">Delete</button>
<span class="font-bold">{{ token }}</span>
<button class="btn btn-red" @click="deleteUserToken(token)">
Delete
</button>
</li>
<div
v-if="deleteUserTokenError"
@ -102,8 +117,10 @@
</svg>
</div>
<div>
<p class="font-bold">User token created. Copy it now since it won't be showed again</p>
<p class="text-sm">{{token}}</p>
<p class="font-bold">
User token created. Copy it now since it won't be showed again
</p>
<p class="text-sm">{{ token }}</p>
</div>
</div>
<span
@ -130,8 +147,10 @@
type="text"
placeholder="Token name"
v-model="newtokenname"
>
<button class="ml-3 btn btn-blue" @click="createUserToken()">Create Token</button>
/>
<button class="ml-3 btn btn-blue" @click="createUserToken()">
Create Token
</button>
</div>
<div
v-if="createUserTokenError"
@ -151,14 +170,14 @@ import {
fetchRemoteSources,
createUserToken,
deleteUserToken,
deleteLinkedAccount
} from "@/util/data.js";
deleteLinkedAccount,
} from '@/util/data.js';
import { userAddLinkedAccountLink } from "@/util/link.js";
import { userAddLinkedAccountLink } from '@/util/link.js';
export default {
components: {},
name: "usersettings",
name: 'usersettings',
props: {},
data() {
return {
@ -169,7 +188,7 @@ export default {
remotesources: [],
token: null,
newtokenname: null,
selectedRemoteSourceName: null
selectedRemoteSourceName: null,
};
},
methods: {
@ -181,7 +200,7 @@ export default {
async fetchCurrentUser() {
let { data, error } = await fetchCurrentUser();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.user = data;
@ -189,7 +208,7 @@ export default {
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.remotesources = data;
@ -249,15 +268,13 @@ export default {
return;
}
this.fetchCurrentUser();
}
},
},
created: function() {
created: function () {
this.fetchCurrentUser();
this.fetchRemoteSources();
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -18,21 +18,22 @@
</div>
<div class="flex" v-for="variable in variables" v-bind:key="variable.id">
<div class="w-2/12">
<span class="name">{{variable.name}}</span>
<div v-if="showparentpath" class="text-sm font-light">from {{variable.parent_path}}</div>
<span class="name">{{ variable.name }}</span>
<div v-if="showparentpath" class="text-sm font-light">
from {{ variable.parent_path }}
</div>
</div>
<div class="w-10/12">
<div class="flex" v-for="val in variable.values" v-bind:key="val.id">
<div class="w-2/12">
<span>{{val.secret_name}}</span>
<div
v-if="val.matching_secret_parent_path"
class="text-sm"
>using secret from {{val.matching_secret_parent_path}}</div>
<span>{{ val.secret_name }}</span>
<div v-if="val.matching_secret_parent_path" class="text-sm">
using secret from {{ val.matching_secret_parent_path }}
</div>
<div v-else class="text-sm text-red-600">no matching secret</div>
</div>
<div class="w-2/12">
<span>{{val.secret_var}}</span>
<span>{{ val.secret_var }}</span>
</div>
<div class="w-8/12">
<div v-if="val.when">
@ -43,13 +44,19 @@
<span>{{ type }}</span>
</div>
<div class="w-1/3">
<div v-for="include in val.when[type].include" v-bind:key="include.match">
<div>{{include.match}}</div>
<div
v-for="include in val.when[type].include"
v-bind:key="include.match"
>
<div>{{ include.match }}</div>
</div>
</div>
<div class="w-1/3">
<div v-for="exclude in val.when[type].exclude" v-bind:key="exclude.match">
<div>{{exclude.match}}</div>
<div
v-for="exclude in val.when[type].exclude"
v-bind:key="exclude.match"
>
<div>{{ exclude.match }}</div>
</div>
</div>
</div>
@ -66,15 +73,12 @@
<script>
export default {
components: {},
name: "vars",
name: 'vars',
props: {
variables: Array,
showparentpath: Boolean
}
showparentpath: Boolean,
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -1,7 +1,7 @@
@tailwind base;
@tailwind components;
@import "@/css/_ansi.scss";
@import '@/css/_ansi.scss';
.btn {
@apply font-bold py-2 px-4 rounded;
@ -119,7 +119,7 @@
border-radius: 290486px;
border-right-color: transparent;
border-top-color: transparent;
content: "";
content: '';
display: block;
width: 1em;
height: 1em;

View File

@ -1,11 +1,11 @@
import "@/css/tailwind.scss";
import { getUser } from "@/util/auth";
import "@mdi/font/css/materialdesignicons.css";
import Vue from "vue";
import Vue2Filters from "vue2-filters";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import '@/css/tailwind.scss';
import { getUser } from '@/util/auth';
import '@mdi/font/css/materialdesignicons.css';
import Vue from 'vue';
import Vue2Filters from 'vue2-filters';
import App from './App.vue';
import router from './router';
import store from './store';
Vue.use(Vue2Filters);
@ -13,12 +13,12 @@ Vue.use(Vue2Filters);
new Vue({
router,
store,
created: function() {
created: function () {
let user = getUser();
if (user) {
store.dispatch("setUser", user);
store.dispatch('setUser', user);
}
store.dispatch("setRegisterUser", null);
store.dispatch('setRegisterUser', null);
},
render: h => h(App)
}).$mount("#app");
render: (h) => h(App),
}).$mount('#app');

View File

@ -1,243 +1,348 @@
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "./views/Home.vue";
import User from "./views/User.vue";
import Org from "./views/Org.vue";
import Project from "./views/Project.vue";
import ProjectGroup from "./views/ProjectGroup.vue";
import AddLinkedAccount from "./views/AddLinkedAccount.vue";
import usersettings from "./components/usersettings.vue";
import projects from "./components/projects.vue";
import projectsettings from "./components/projectsettings.vue";
import projectgroupsettings from "./components/projectgroupsettings.vue";
import createproject from "./components/createproject.vue";
import createprojectgroup from "./components/createprojectgroup.vue";
import createorganization from "./components/createorganization.vue";
import orgmembers from "./components/orgmembers.vue";
import runs from "./components/runs.vue";
import runsummary from "./components/runsummary.vue";
import tasksummary from "./components/tasksummary.vue";
import Oauth2 from "./views/Oauth2.vue";
import Register from "./views/Register.vue";
import Login from "./views/Login.vue";
import Logout from "./views/Logout.vue";
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from './views/Home.vue';
import User from './views/User.vue';
import Org from './views/Org.vue';
import Project from './views/Project.vue';
import ProjectGroup from './views/ProjectGroup.vue';
import AddLinkedAccount from './views/AddLinkedAccount.vue';
import usersettings from './components/usersettings.vue';
import projects from './components/projects.vue';
import projectsettings from './components/projectsettings.vue';
import projectgroupsettings from './components/projectgroupsettings.vue';
import createproject from './components/createproject.vue';
import createprojectgroup from './components/createprojectgroup.vue';
import createorganization from './components/createorganization.vue';
import orgmembers from './components/orgmembers.vue';
import runs from './components/runs.vue';
import runsummary from './components/runsummary.vue';
import tasksummary from './components/tasksummary.vue';
import Oauth2 from './views/Oauth2.vue';
import Register from './views/Register.vue';
import Login from './views/Login.vue';
import Logout from './views/Logout.vue';
import CreateSource from './views/CreateSource.vue';
import { parseRef, projectRunLink } from "@/util/link.js";
import { fetchProject } from "@/util/data.js";
import { parseRef, projectRunLink } from '@/util/link.js';
import { fetchProject } from '@/util/data.js';
import store from "./store";
import store from './store';
Vue.use(VueRouter);
const router = new VueRouter({
mode: "history",
mode: 'history',
routes: [
{
path: "/register",
name: "register",
path: '/register',
name: 'register',
component: Register,
},
{
path: "/login",
name: "login",
component: Login
path: '/newsource',
name: 'newsource',
component: CreateSource,
},
{
path: "/logout",
name: "logout",
component: Logout
path: '/login',
name: 'login',
component: Login,
},
{
path: "/oauth2/callback",
name: "oauth2callback",
component: Oauth2
path: '/logout',
name: 'logout',
component: Logout,
},
{
path: "/",
name: "home",
component: Home
path: '/oauth2/callback',
name: 'oauth2callback',
component: Oauth2,
},
{
path: "/neworganization",
path: '/',
name: 'home',
component: Home,
},
{
path: '/neworganization',
component: createorganization,
},
{
path: "/user/:username",
path: '/user/:username',
component: User,
props: (route) => ({ username: route.params.username }),
children: [
{
path: "",
name: "user",
path: '',
name: 'user',
component: projects,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
{
path: "projects",
name: "user projects",
path: 'projects',
name: 'user projects',
component: projects,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
{
path: "runs",
name: "user direct runs",
path: 'runs',
name: 'user direct runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
{
path: "runs/:runid",
name: "user direct run",
path: 'runs/:runid',
name: 'user direct run',
component: runsummary,
props: (route) => ({ ownertype: "user", ownername: route.params.username, runid: route.params.runid })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
runid: route.params.runid,
}),
},
{
path: "runs/:runid/tasks/:taskid",
name: "user direct run task",
path: 'runs/:runid/tasks/:taskid',
name: 'user direct run task',
component: tasksummary,
props: (route) => ({ ownertype: "user", ownername: route.params.username, runid: route.params.runid, taskid: route.params.taskid })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
runid: route.params.runid,
taskid: route.params.taskid,
}),
},
{
path: "settings",
name: "user settings",
path: 'settings',
name: 'user settings',
component: usersettings,
props: (route) => ({ username: route.params.username }),
},
{
path: "linkedaccounts/add/:remotesource",
name: "user add linked account",
path: 'linkedaccounts/add/:remotesource',
name: 'user add linked account',
component: AddLinkedAccount,
props: (route) => ({ username: route.params.username, remoteSourceName: route.params.remotesource })
props: (route) => ({
username: route.params.username,
remoteSourceName: route.params.remotesource,
}),
},
{
path: "createprojectgroup",
name: "user create project group",
path: 'createprojectgroup',
name: 'user create project group',
component: createprojectgroup,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
{
path: "createproject",
name: "user create project",
path: 'createproject',
name: 'user create project',
component: createproject,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
]
],
},
{
path: "/user/:username/projects/:projectref(.*\\.proj)",
path: '/user/:username/projects/:projectref(.*\\.proj)',
component: Project,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref) }),
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
}),
children: [
{
path: "",
name: "user project",
path: '',
name: 'user project',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
}),
},
{
path: "runs",
name: "user project runs",
path: 'runs',
name: 'user project runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
}),
},
{
path: "branches",
name: "user project branches runs",
path: 'branches',
name: 'user project branches runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), query: "branches" })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
query: 'branches',
}),
},
{
path: "tags",
name: "user project tags runs",
path: 'tags',
name: 'user project tags runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), query: "tags" })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
query: 'tags',
}),
},
{
path: "pullrequests",
name: "user project pull requests runs",
path: 'pullrequests',
name: 'user project pull requests runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), query: "pullrequests" })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
query: 'pullrequests',
}),
},
{
path: "runs/:runid",
name: "user project run",
path: 'runs/:runid',
name: 'user project run',
component: runsummary,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), runid: route.params.runid })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
runid: route.params.runid,
}),
},
{
path: "runs/:runid/tasks/:taskid",
name: "user project run task",
path: 'runs/:runid/tasks/:taskid',
name: 'user project run task',
component: tasksummary,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), runid: route.params.runid, taskid: route.params.taskid })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
runid: route.params.runid,
taskid: route.params.taskid,
}),
},
{
path: "settings",
name: "user project settings",
path: 'settings',
name: 'user project settings',
component: projectsettings,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
}),
},
]
],
},
{
path: "/user/:username/projectgroups/:projectgroupref(.*\\.proj)",
path: '/user/:username/projectgroups/:projectgroupref(.*\\.proj)',
component: ProjectGroup,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) }),
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
children: [
{
path: "",
name: "user project group",
path: '',
name: 'user project group',
component: projects,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) }),
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "projects",
name: "user project group projects",
path: 'projects',
name: 'user project group projects',
component: projects,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "settings",
name: "user project group settings",
path: 'settings',
name: 'user project group settings',
component: projectgroupsettings,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "createprojectgroup",
name: "user project group create project group",
path: 'createprojectgroup',
name: 'user project group create project group',
component: createprojectgroup,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "createproject",
name: "user project group create project",
path: 'createproject',
name: 'user project group create project',
component: createproject,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
]
],
},
{
path: "/org/:orgname",
path: '/org/:orgname',
component: Org,
props: (route) => ({ orgname: route.params.orgname }),
children: [
{
path: "",
name: "org",
path: '',
name: 'org',
component: projects,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
}),
},
{
path: "projects",
name: "org projects",
path: 'projects',
name: 'org projects',
component: projects,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
}),
},
{
path: "members",
name: "org members",
path: 'members',
name: 'org members',
component: orgmembers,
props: (route) => ({ orgname: route.params.orgname })
props: (route) => ({ orgname: route.params.orgname }),
},
/* {
path: "settings",
@ -246,141 +351,213 @@ const router = new VueRouter({
props: (route) => ({ username: route.params.username }),
}, */
{
path: "createprojectgroup",
name: "org create project group",
path: 'createprojectgroup',
name: 'org create project group',
component: createprojectgroup,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
}),
},
{
path: "createproject",
name: "org create project",
path: 'createproject',
name: 'org create project',
component: createproject,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
}),
},
]
],
},
{
path: "/org/:orgname/projects/:projectref(.*\\.proj)",
path: '/org/:orgname/projects/:projectref(.*\\.proj)',
component: Project,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref) }),
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
}),
children: [
{
path: "",
name: "org project",
path: '',
name: 'org project',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
}),
},
{
path: "runs",
name: "org project runs",
path: 'runs',
name: 'org project runs',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
}),
},
{
path: "branches",
name: "org project branches runs",
path: 'branches',
name: 'org project branches runs',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), query: "branches" })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
query: 'branches',
}),
},
{
path: "tags",
name: "org project tags runs",
path: 'tags',
name: 'org project tags runs',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), query: "tags" })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
query: 'tags',
}),
},
{
path: "pullrequests",
name: "org project pull requests runs",
path: 'pullrequests',
name: 'org project pull requests runs',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), query: "pullrequests" })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
query: 'pullrequests',
}),
},
{
path: "runs/:runid",
name: "org project run",
path: 'runs/:runid',
name: 'org project run',
component: runsummary,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), runid: route.params.runid })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
runid: route.params.runid,
}),
},
{
path: "runs/:runid/tasks/:taskid",
name: "org project run task",
path: 'runs/:runid/tasks/:taskid',
name: 'org project run task',
component: tasksummary,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), runid: route.params.runid, taskid: route.params.taskid })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
runid: route.params.runid,
taskid: route.params.taskid,
}),
},
{
path: "settings",
name: "org project settings",
path: 'settings',
name: 'org project settings',
component: projectsettings,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
}),
},
]
],
},
{
path: "/org/:orgname/projectgroups/:projectgroupref(.*\\.proj)",
path: '/org/:orgname/projectgroups/:projectgroupref(.*\\.proj)',
component: ProjectGroup,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) }),
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
children: [
{
path: "",
name: "org project group",
path: '',
name: 'org project group',
component: projects,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) }),
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "projects",
name: "org project group projects",
path: 'projects',
name: 'org project group projects',
component: projects,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "settings",
name: "org project group settings",
path: 'settings',
name: 'org project group settings',
component: projectgroupsettings,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "createprojectgroup",
name: "org project group create project group",
path: 'createprojectgroup',
name: 'org project group create project group',
component: createprojectgroup,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "createproject",
name: "org project group create project",
path: 'createproject',
name: 'org project group create project',
component: createproject,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
]
],
},
]
],
});
router.beforeEach(async (to, from, next) => {
store.dispatch("setError", null);
store.dispatch('setError', null);
const { path, query } = to
const { path, query } = to;
if (path == "/run") {
if (path == '/run') {
// generic run handler by projectref and runid
let projectref = query.projectref
let runid = query.runid
let projectref = query.projectref;
let runid = query.runid;
let { data, error } = await fetchProject(projectref);
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
let project = data;
let parts = project.path.split("/")
let nextPath = projectRunLink(parts[0], parts[1], parts.slice(2), runid)
let parts = project.path.split('/');
let nextPath = projectRunLink(parts[0], parts[1], parts.slice(2), runid);
next({ path: nextPath.path, replace: true })
next({ path: nextPath.path, replace: true });
}
next()
})
next();
});
export default router
export default router;

View File

@ -1,55 +1,54 @@
import Vue from 'vue'
import Vuex from 'vuex'
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)
Vue.use(Vuex);
const state = {
error: null,
user: null,
registeruser: null,
}
error: null,
user: null,
registeruser: null,
};
const getters = {
// global error
error: state => {
return state.error
},
user: state => {
return state.user
},
registeruser: state => {
return state.registeruser
}
}
// global error
error: (state) => {
return state.error;
},
user: (state) => {
return state.user;
},
registeruser: (state) => {
return state.registeruser;
},
};
const mutations = {
setError(state, error) {
state.error = error
},
setUser(state, user) {
state.user = user
},
setRegisterUser(state, user) {
state.registeruser = user
}
}
setError(state, error) {
state.error = error;
},
setUser(state, user) {
state.user = user;
},
setRegisterUser(state, user) {
state.registeruser = user;
},
};
const actions = {
setError({ commit }, error) {
commit('setError', error)
},
setUser({ commit }, user) {
commit('setUser', user)
},
setRegisterUser({ commit }, user) {
commit('setRegisterUser', user)
}
}
setError({ commit }, error) {
commit('setError', error);
},
setUser({ commit }, user) {
commit('setUser', user);
},
setRegisterUser({ commit }, user) {
commit('setRegisterUser', user);
},
};
export default new Vuex.Store({
state,
getters,
actions,
mutations,
})
state,
getters,
actions,
mutations,
});

View File

@ -1,8 +1,8 @@
import store from "@/store";
import store from '@/store';
const ID_TOKEN_KEY = "id_token";
const USER_KEY = "user";
const LOGIN_REDIRECT_KEY = "login_redirect";
const ID_TOKEN_KEY = 'id_token';
const USER_KEY = 'user';
const LOGIN_REDIRECT_KEY = 'login_redirect';
let API_URL = window.CONFIG.API_URL;
let API_BASE_PATH = window.CONFIG.API_BASE_PATH;
@ -10,20 +10,20 @@ let API_BASE_PATH = window.CONFIG.API_BASE_PATH;
export function setLoggedUser(token, user) {
setIdToken(token);
setUser(user);
store.dispatch("setUser", user);
store.dispatch('setUser', user);
}
export function doLogout() {
unsetIdToken();
unsetUser();
store.dispatch("setUser", null);
store.dispatch('setUser', null);
}
export function apiurlwithtoken(path) {
let u = new URL(API_URL + API_BASE_PATH + path);
let idToken = getIdToken();
if (idToken) {
u.searchParams.append("access_token", idToken);
u.searchParams.append('access_token', idToken);
}
return u;
}
@ -33,19 +33,19 @@ export function apiurl(path) {
}
export function loginurl() {
return apiurl("/auth/login");
return apiurl('/auth/login');
}
export function authorizeurl() {
return apiurl("/auth/authorize");
return apiurl('/auth/authorize');
}
export function registerurl() {
return new apiurl("/auth/register");
return new apiurl('/auth/register');
}
export function oauth2callbackurl() {
return new apiurl("/auth/oauth2/callback");
return new apiurl('/auth/oauth2/callback');
}
export async function loginapi(init) {
@ -64,19 +64,19 @@ export async function registerapi(init) {
return await window.fetch(registerurl(), init);
}
export async function fetch(url, init, signal) {
export async function fetch(url, init, signal, token, tokenType = 'bearer') {
if (!init) {
init = {};
}
if (init.headers === undefined) {
init["headers"] = {};
init['headers'] = {};
}
if (signal) {
init["signal"] = signal;
init['signal'] = signal;
}
let idToken = getIdToken();
let idToken = token || getIdToken();
if (idToken) {
init.headers["Authorization"] = "bearer " + idToken;
init.headers['Authorization'] = tokenType + ' ' + idToken;
}
return await window.fetch(url, init);

View File

@ -1,14 +1,18 @@
import router from "@/router";
import { apiurl, fetch as authfetch, loginapi, registerapi } from "@/util/auth";
import router from '@/router';
import { apiurl, fetch as authfetch, loginapi, registerapi } from '@/util/auth';
export async function fetch(url, init, signal) {
export const GITHUB_API_URL = 'https://api.github.com';
export const GITHUB_SSH_KEY =
'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==';
export async function fetch(url, init, signal, token, tokenType) {
try {
let res = await authfetch(url, init, signal);
let res = await authfetch(url, init, signal, token, tokenType);
if (!res.ok) {
if (res.status === 401) {
router.push({
name: "login",
query: { redirect: router.currentRoute.fullPath }
name: 'login',
query: { redirect: router.currentRoute.fullPath },
});
// if we return a response containing an error what happens is
// that router.push mounts the login view before the calling
@ -33,21 +37,21 @@ export async function fetch(url, init, signal) {
return { data: await res.json(), error: null };
}
} catch (e) {
if (e.name == "AbortError") {
if (e.name == 'AbortError') {
return { data: null, error: null, aborted: true };
}
return { data: null, error: "api call failed: " + e.message };
return { data: null, error: 'api call failed: ' + e.message };
}
}
export async function login(username, password, remotesourcename) {
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
remote_source_name: remotesourcename,
login_name: username,
password: password
})
password: password,
}),
};
try {
@ -59,7 +63,7 @@ export async function login(username, password, remotesourcename) {
return { data: await res.json(), error: null };
}
} catch (e) {
return { data: null, error: "api call failed: " + e.message };
return { data: null, error: 'api call failed: ' + e.message };
}
}
@ -70,13 +74,13 @@ export async function register(
remotepassword
) {
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
username: username,
remote_source_name: remotesourcename,
remote_source_login_name: remoteloginname,
remote_source_login_password: remotepassword
})
remote_source_login_password: remotepassword,
}),
};
try {
@ -88,131 +92,166 @@ export async function register(
return { data: await res.json(), error: null };
}
} catch (e) {
return { data: null, error: "api call failed: " + e.message };
return { data: null, error: 'api call failed: ' + e.message };
}
}
export async function fetchCurrentUser(signal) {
let path = "/user";
let path = '/user';
return await fetch(apiurl(path), null, signal);
}
export async function fetchOrgMembers(orgref, signal) {
let path = "/orgs/" + orgref + "/members";
let path = '/orgs/' + orgref + '/members';
return await fetch(apiurl(path), null, signal);
}
export async function fetchRuns(group, startRunID, lastrun, signal) {
let u = apiurl("/runs");
let u = apiurl('/runs');
if (group) {
u.searchParams.append("group", group);
u.searchParams.append('group', group);
}
if (lastrun) {
u.searchParams.append("lastrun", true);
u.searchParams.append('lastrun', true);
}
if (startRunID) {
u.searchParams.append("start", startRunID);
u.searchParams.append('start', startRunID);
}
return await fetch(u, null, signal);
}
export async function fetchRun(runid, signal) {
return await fetch(apiurl("/runs/" + runid), null, signal);
return await fetch(apiurl('/runs/' + runid), null, signal);
}
export async function fetchTask(runid, taskid, signal) {
return await fetch(apiurl("/runs/" + runid + "/tasks/" + taskid), signal);
return await fetch(apiurl('/runs/' + runid + '/tasks/' + taskid), signal);
}
export async function fetchUser(username, signal) {
let path = "/users/" + username;
let path = '/users/' + username;
return await fetch(apiurl(path), null, signal);
}
export async function fetchProjectGroup(projectgroupref, signal) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
return await fetch(apiurl(path), null, signal);
}
export async function fetchProjectGroupSubgroups(projectgroupref, signal) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
path += "/subgroups";
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
path += '/subgroups';
return await fetch(apiurl(path), null, signal);
}
export async function fetchProjectGroupProjects(projectgroupref, signal) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
path += "/projects";
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
path += '/projects';
return await fetch(apiurl(path), null, signal);
}
export async function fetchProject(ref, signal) {
let path = "/projects/" + encodeURIComponent(ref);
let path = '/projects/' + encodeURIComponent(ref);
return await fetch(apiurl(path), null, signal);
}
export async function fetchSecrets(ownertype, ref, all, signal) {
let path;
if (ownertype == "project") {
path = "/projects/";
} else if (ownertype == "projectgroup") {
path = "/projectgroups/";
if (ownertype == 'project') {
path = '/projects/';
} else if (ownertype == 'projectgroup') {
path = '/projectgroups/';
}
path += encodeURIComponent(ref);
path += "/secrets";
path += '/secrets';
if (all) {
path += "?tree&removeoverridden";
path += '?tree&removeoverridden';
}
return await fetch(apiurl(path), null, signal);
}
export async function fetchVariables(ownertype, ref, all, signal) {
let path;
if (ownertype == "project") {
path = "/projects/";
} else if (ownertype == "projectgroup") {
path = "/projectgroups/";
if (ownertype == 'project') {
path = '/projects/';
} else if (ownertype == 'projectgroup') {
path = '/projectgroups/';
}
path += encodeURIComponent(ref);
path += "/variables";
path += '/variables';
if (all) {
path += "?tree&removeoverridden";
path += '?tree&removeoverridden';
}
return await fetch(apiurl(path), null, signal);
}
export async function createOrganization(orgname, visibility, signal) {
let path = "/orgs";
export async function createRemoteSource(
token,
type,
name,
clientID,
clientSecret,
apiURL,
authType,
skipVerify,
sshHostKey,
skipSshHostKeyCheck,
registrationEnabled,
loginEnabled,
signal
) {
let path = '/remotesources';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
name,
apiurl: apiURL,
type,
auth_type: authType,
skip_verify: skipVerify,
ssh_host_key: sshHostKey,
skip_ssh_host_key_check: skipSshHostKeyCheck,
oauth_2_client_id: clientID,
oauth_2_client_secret: clientSecret,
registration_enabled: registrationEnabled,
login_enabled: loginEnabled,
}),
};
return await fetch(apiurl(path), init, signal, token, 'token');
}
export async function createOrganization(orgname, visibility, signal) {
let path = '/orgs';
let init = {
method: 'POST',
body: JSON.stringify({
name: orgname,
visibility: visibility
})
visibility: visibility,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function createUserToken(username, tokenname, signal) {
let path = "/users/" + username + "/tokens";
let path = '/users/' + username + '/tokens';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
token_name: tokenname
})
token_name: tokenname,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function deleteUserToken(username, tokenname, signal) {
let path = "/users/" + username + "/tokens/" + tokenname;
let path = '/users/' + username + '/tokens/' + tokenname;
let init = {
method: "DELETE"
method: 'DELETE',
};
return await fetch(apiurl(path), init, signal);
}
@ -224,90 +263,90 @@ export async function createUserLinkedAccount(
password,
signal
) {
let path = "/users/" + username + "/linkedaccounts";
let path = '/users/' + username + '/linkedaccounts';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
remote_source_name: remotesourcename,
remote_source_login_name: loginname,
remote_source_login_password: password
})
remote_source_login_password: password,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function deleteLinkedAccount(username, laid, signal) {
let path = "/users/" + username + "/linkedaccounts/" + laid;
let path = '/users/' + username + '/linkedaccounts/' + laid;
let init = {
method: "DELETE"
method: 'DELETE',
};
return await fetch(apiurl(path), init, signal);
}
export async function restartRun(runid, fromStart, signal) {
let path = "/runs/" + runid + "/actions";
let path = '/runs/' + runid + '/actions';
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
action_type: "restart",
from_start: fromStart
})
action_type: 'restart',
from_start: fromStart,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function cancelRun(runid, signal) {
let path = "/runs/" + runid + "/actions";
let path = '/runs/' + runid + '/actions';
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
action_type: "cancel"
})
action_type: 'cancel',
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function stopRun(runid, signal) {
let path = "/runs/" + runid + "/actions";
let path = '/runs/' + runid + '/actions';
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
action_type: "stop"
})
action_type: 'stop',
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function approveTask(runid, taskid, signal) {
let path = "/runs/" + runid + "/tasks/" + taskid + "/actions";
let path = '/runs/' + runid + '/tasks/' + taskid + '/actions';
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
action_type: "approve"
})
action_type: 'approve',
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function fetchRemoteSources(signal) {
let path = "/remotesources";
let path = '/remotesources';
return await fetch(apiurl(path), null, signal);
}
export async function userRemoteRepos(remotesourceid, signal) {
let path = "/user/remoterepos/" + remotesourceid;
let path = '/user/remoterepos/' + remotesourceid;
return await fetch(apiurl(path, null, signal));
}
export async function createProjectGroup(parentref, name, visibility, signal) {
let path = "/projectgroups";
let path = '/projectgroups';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
name: name,
parent_ref: parentref,
visibility: visibility
})
visibility: visibility,
}),
};
return await fetch(apiurl(path), init, signal);
}
@ -318,13 +357,13 @@ export async function updateProjectGroup(
visibility,
signal
) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
name: name,
visibility: visibility
})
visibility: visibility,
}),
};
return await fetch(apiurl(path), init, signal);
}
@ -338,55 +377,61 @@ export async function createProject(
passvarstoforkedpr,
signal
) {
let path = "/projects";
let path = '/projects';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
name: name,
parent_ref: parentref,
visibility: visibility,
remote_source_name: remotesourcename,
repo_path: remoterepopath,
pass_vars_to_forked_pr: passvarstoforkedpr
})
pass_vars_to_forked_pr: passvarstoforkedpr,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function updateProject(projectref, name, visibility, passvarstoforkedpr, signal) {
let path = "/projects/" + encodeURIComponent(projectref);
export async function updateProject(
projectref,
name,
visibility,
passvarstoforkedpr,
signal
) {
let path = '/projects/' + encodeURIComponent(projectref);
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
name: name,
visibility: visibility,
pass_vars_to_forked_pr: passvarstoforkedpr
})
pass_vars_to_forked_pr: passvarstoforkedpr,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function deleteProject(projectref, signal) {
let path = "/projects/" + encodeURIComponent(projectref);
let path = '/projects/' + encodeURIComponent(projectref);
let init = {
method: "DELETE"
method: 'DELETE',
};
return await fetch(apiurl(path), init, signal);
}
export async function projectUpdateRepoLinkedAccount(projectref, signal) {
let path =
"/projects/" + encodeURIComponent(projectref) + "/updaterepolinkedaccount";
'/projects/' + encodeURIComponent(projectref) + '/updaterepolinkedaccount';
let init = {
method: "PUT"
method: 'PUT',
};
return await fetch(apiurl(path), init, signal);
}
export async function deleteProjectGroup(projectgroupref, signal) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
let init = {
method: "DELETE"
method: 'DELETE',
};
return await fetch(apiurl(path), init, signal);
}

View File

@ -1,130 +1,173 @@
export function parseRef(ref) {
ref = ref.replace(/\.proj/, "")
// return empty array or split return an array with the empty element
if (!ref) {
return []
}
return ref.split("/")
ref = ref.replace(/\.proj/, '');
// return empty array or split return an array with the empty element
if (!ref) {
return [];
}
return ref.split('/');
}
export function ownerLink(ownertype, ownername) {
if (ownertype == "user") {
return { name: ownertype, params: { username: ownername } }
} else if (ownertype == "org") {
return { name: ownertype, params: { orgname: ownername } }
}
if (ownertype == 'user') {
return { name: ownertype, params: { username: ownername } };
} else if (ownertype == 'org') {
return { name: ownertype, params: { orgname: ownername } };
}
}
export function ownerProjectsLink(ownertype, ownername) {
return { name: ownertype + " projects", params: { ownername: ownername } }
return { name: ownertype + ' projects', params: { ownername: ownername } };
}
export function ownerSettingsLink(ownertype, ownername) {
if (ownertype == "user") {
return { name: ownertype + " settings", params: { username: ownername } }
} else if (ownertype == "org") {
return { name: ownertype + " settings", params: { orgname: ownername } }
}
if (ownertype == 'user') {
return { name: ownertype + ' settings', params: { username: ownername } };
} else if (ownertype == 'org') {
return { name: ownertype + ' settings', params: { orgname: ownername } };
}
}
export function userDirectRunsLink(username) {
return { name: "user direct runs", params: { username: username } }
return { name: 'user direct runs', params: { username: username } };
}
export function userDirectRunLink(username, runid) {
return { name: "user direct run", params: { username: username, runid: runid } }
return {
name: 'user direct run',
params: { username: username, runid: runid },
};
}
export function userDirectRunTaskLink(username, runid, taskid) {
return { name: "user direct run task", params: { username: username, runid: runid, taskid: taskid } }
return {
name: 'user direct run task',
params: { username: username, runid: runid, taskid: taskid },
};
}
export function userAddLinkedAccountLink(username, remotesourcename) {
return { name: "user add linked account", params: { username: username, remotesource: remotesourcename } }
return {
name: 'user add linked account',
params: { username: username, remotesource: remotesourcename },
};
}
export function orgMembersLink(orgname) {
return { name: "org members", params: { orgname: orgname } }
return { name: 'org members', params: { orgname: orgname } };
}
// Note, when creating a router link containing a project/projectgroup ref (a
// path), unfortunately, we cannot use route name and params since it will path
// escape it
export function projectGroupPath(ownertype, ownername, projectgroupref) {
let path = `/${ownertype}/${ownername}`
// root project group will have a .proj without a name
let projectgrouppath = (projectgroupref.join("/") + ".proj")
path = `${path}/projectgroups/${projectgrouppath}`
return path
let path = `/${ownertype}/${ownername}`;
// root project group will have a .proj without a name
let projectgrouppath = projectgroupref.join('/') + '.proj';
path = `${path}/projectgroups/${projectgrouppath}`;
return path;
}
export function projectPath(ownertype, ownername, projectref) {
let path = `/${ownertype}/${ownername}`
let projectpath = (projectref.join("/") + ".proj")
path = `${path}/projects/${projectpath}`
return path
let path = `/${ownertype}/${ownername}`;
let projectpath = projectref.join('/') + '.proj';
path = `${path}/projects/${projectpath}`;
return path;
}
export function projectGroupLink(ownertype, ownername, projectgroupref) {
return { path: projectGroupPath(ownertype, ownername, projectgroupref) }
return { path: projectGroupPath(ownertype, ownername, projectgroupref) };
}
export function projectGroupProjectsLink(ownertype, ownername, projectgroupref) {
let projectgrouppath = (projectgroupref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projectgroups/${projectgrouppath}/projects` }
export function projectGroupProjectsLink(
ownertype,
ownername,
projectgroupref
) {
let projectgrouppath = projectgroupref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projectgroups/${projectgrouppath}/projects`,
};
}
export function projectLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}` }
let projectpath = projectref.join('/') + '.proj';
return { path: `/${ownertype}/${ownername}/projects/${projectpath}` };
}
export function projectRunsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/runs` }
let projectpath = projectref.join('/') + '.proj';
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/runs` };
}
export function projectBranchesRunsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/branches` }
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/branches`,
};
}
export function projectTagsRunsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/tags` }
let projectpath = projectref.join('/') + '.proj';
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/tags` };
}
export function projectPRsRunsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/pullrequests` }
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/pullrequests`,
};
}
export function projectRunLink(ownertype, ownername, projectref, runid) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/runs/${runid}` }
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/runs/${runid}`,
};
}
export function projectRunTaskLink(ownertype, ownername, projectref, runid, taskid) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/runs/${runid}/tasks/${taskid}` }
export function projectRunTaskLink(
ownertype,
ownername,
projectref,
runid,
taskid
) {
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/runs/${runid}/tasks/${taskid}`,
};
}
export function projectGroupSettingsLink(ownertype, ownername, projectgroupref) {
let path = projectGroupPath(ownertype, ownername, projectgroupref)
return { path: `${path}/settings` }
export function projectGroupSettingsLink(
ownertype,
ownername,
projectgroupref
) {
let path = projectGroupPath(ownertype, ownername, projectgroupref);
return { path: `${path}/settings` };
}
export function projectSettingsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/settings` }
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/settings`,
};
}
export function projectGroupCreateProjectGroupLink(ownertype, ownername, projectgroupref) {
let path = projectGroupPath(ownertype, ownername, projectgroupref)
return { path: `${path}/createprojectgroup` }
export function projectGroupCreateProjectGroupLink(
ownertype,
ownername,
projectgroupref
) {
let path = projectGroupPath(ownertype, ownername, projectgroupref);
return { path: `${path}/createprojectgroup` };
}
export function projectGroupCreateProjectLink(ownertype, ownername, projectgroupref) {
let path = projectGroupPath(ownertype, ownername, projectgroupref)
return { path: `${path}/createproject` }
}
export function projectGroupCreateProjectLink(
ownertype,
ownername,
projectgroupref
) {
let path = projectGroupPath(ownertype, ownername, projectgroupref);
return { path: `${path}/createproject` };
}

View File

@ -1,25 +1,24 @@
export function runStatus(run) {
// * if the run has a result then return the result
// * if stopping return "stopping"
// * return the phase
if (run.result != "unknown") return run.result;
if (run.stopping) return "stopping";
if (run.phase != "finished") return run.phase;
// * if the run has a result then return the result
// * if stopping return "stopping"
// * return the phase
if (run.result != 'unknown') return run.result;
if (run.stopping) return 'stopping';
if (run.phase != 'finished') return run.phase;
return run.result;
return run.result;
}
export function runResultClass(run) {
let status = runStatus(run);
let status = runStatus(run);
if (status == "setuperror") return "setuperror";
if (status == "queued") return "unknown";
if (status == "cancelled") return "failed";
if (status == "running") return "running";
if (status == "stopping") return "failed";
if (status == "stopped") return "failed";
if (status == "success") return "success";
if (status == "failed") return "failed";
return "unknown";
}
if (status == 'setuperror') return 'setuperror';
if (status == 'queued') return 'unknown';
if (status == 'cancelled') return 'failed';
if (status == 'running') return 'running';
if (status == 'stopping') return 'failed';
if (status == 'stopped') return 'failed';
if (status == 'success') return 'success';
if (status == 'failed') return 'failed';
return 'unknown';
}

View File

@ -13,36 +13,44 @@
v-if="remotesource.auth_type == 'password'"
action="Add Linked Account"
:name="remotesource.name"
v-on:login="doAddLinkedAccount(remotesource.name, $event.username, $event.password)"
v-on:login="
doAddLinkedAccount(
remotesource.name,
$event.username,
$event.password
)
"
/>
<button
v-else
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
@click="doAddLinkedAccount(remotesource.name)"
>Add Linked Account with {{remotesource.name}}</button>
>
Add Linked Account with {{ remotesource.name }}
</button>
</div>
</div>
</div>
</template>
<script>
import LoginForm from "@/components/loginform";
import LoginForm from '@/components/loginform';
import { fetchRemoteSources, createUserLinkedAccount } from "@/util/data";
import { fetchRemoteSources, createUserLinkedAccount } from '@/util/data';
export default {
name: "AddLinkedAccount",
name: 'AddLinkedAccount',
props: {
username: String,
remoteSourceName: String
remoteSourceName: String,
},
components: {
LoginForm
LoginForm,
},
data: function() {
data: function () {
return {
addLinkedAccountError: null,
remotesource: null
remotesource: null,
};
},
methods: {
@ -52,7 +60,7 @@ export default {
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
for (var i = 0; i < data.length; i++) {
@ -79,17 +87,13 @@ export default {
return;
}
this.$router.push({
name: "user settings",
params: { username: this.username }
name: 'user settings',
params: { username: this.username },
});
}
},
},
created: function() {
created: function () {
this.fetchRemoteSources();
}
},
};
</script>

View File

@ -0,0 +1,70 @@
<template>
<div>
<div
v-if="error"
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
role="alert"
>
<span class="block sm:inline">{{ error }}</span>
</div>
<div>
<div>
<div class="flex justify-center items-center w-max">
<CreateSourceForm v-on:createSource="createSource($event)" />
</div>
</div>
</div>
</div>
</template>
<script>
import CreateSourceForm from '@/components/createsourceform';
import { createRemoteSource } from '@/util/data.js';
import router from '@/router';
export default {
name: 'CreateSource',
components: {
CreateSourceForm,
},
data: function () {
return {
error: null,
};
},
methods: {
async createSource({
token,
name,
type,
clientId,
clientSecret,
url,
skipVerify,
sshHostKey,
skipSshHostKeyCheck,
}) {
const res = await createRemoteSource(
token,
type,
name,
clientId,
clientSecret,
url,
'oauth2',
skipVerify,
sshHostKey,
skipSshHostKeyCheck,
true,
true,
undefined
);
if (res.error) this.$store.dispatch('setError', res.error);
else router.push({ name: 'login' });
},
},
mounted: function () {
this.$store.dispatch('setError', null);
},
};
</script>

View File

@ -1,24 +1,39 @@
<template>
<div class="home"></div>
<div class="home flex flex-col items-center">
<img class="w-64 h-64" src="/img/agola-logo-name.svg" alt="agola logo" />
<h1 class="text-2xl">CI/CD redefined</h1>
<div class="m-8">
<h1 class="text-lg">
Hi, you are almost ready to go! Just
<router-link class="underline text-blue-600" to="/login">
login
</router-link>
into your account or create a
<router-link class="underline text-blue-600" to="/register">
new one
</router-link>
</h1>
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import { mapGetters } from 'vuex';
export default {
name: "Home",
name: 'Home',
components: {},
computed: {
...mapGetters(["user"])
...mapGetters(['user']),
},
created: function() {
created: function () {
let user = this.$store.getters.user;
if (user) {
this.$router.push({
name: "user",
params: { username: this.user.username }
name: 'user',
params: { username: this.user.username },
});
}
}
},
};
</script>

View File

@ -13,6 +13,9 @@
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
>
No remote sources defined
<router-link class="underline text-blue-600 block" to="/newsource">
<button class="btn btn-blue">Create one</button>
</router-link>
</div>
<div
v-else-if="!hasLoginRemoteSources"
@ -53,25 +56,25 @@
</template>
<script>
import { fetchRemoteSources, login } from "@/util/data";
import { fetchRemoteSources, login } from '@/util/data';
import {
setLoggedUser,
unsetLoginRedirect,
setLoginRedirect,
doLogout
} from "@/util/auth";
doLogout,
} from '@/util/auth';
import LoginForm from "@/components/loginform";
import LoginForm from '@/components/loginform';
export default {
name: "Login",
name: 'Login',
components: {
LoginForm
LoginForm,
},
data: function() {
data: function () {
return {
error: null,
remotesources: null
remotesources: null,
};
},
computed: {
@ -88,20 +91,20 @@ export default {
}
}
return false;
}
},
},
methods: {
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.remotesources = data;
},
async doLogin(username, password, remotesourcename) {
unsetLoginRedirect();
let redirect = this.$route.query["redirect"];
let redirect = this.$route.query['redirect'];
this.error = null;
@ -123,18 +126,16 @@ export default {
unsetLoginRedirect();
this.$router.push(redirect);
} else {
this.$router.push({ name: "home" });
this.$router.push({ name: 'home' });
}
}
},
},
mounted: function() {
this.$store.dispatch("setError", null);
mounted: function () {
this.$store.dispatch('setError', null);
},
created: function() {
created: function () {
doLogout();
this.fetchRemoteSources();
}
},
};
</script>

View File

@ -1,13 +1,11 @@
<script>
import { doLogout } from "@/util/auth";
import { doLogout } from '@/util/auth';
export default {
name: "Logout",
created: function() {
name: 'Logout',
created: function () {
doLogout();
this.$router.push("/");
}
this.$router.push('/');
},
};
</script>

View File

@ -11,32 +11,32 @@
</template>
<script>
import { fetch } from "@/util/data";
import { fetch } from '@/util/data';
import {
oauth2callbackurl,
setLoggedUser,
unsetLoginRedirect,
getLoginRedirect
} from "@/util/auth";
getLoginRedirect,
} from '@/util/auth';
export default {
components: {},
name: "Oauth2",
name: 'Oauth2',
props: {},
data() {
return {
error: null,
run: null,
code: this.$route.query.code,
username: null
username: null,
};
},
methods: {
async doOauth2() {
let u = oauth2callbackurl();
u.searchParams.append("code", this.$route.query.code);
u.searchParams.append("state", this.$route.query.state);
u.searchParams.append('code', this.$route.query.code);
u.searchParams.append('state', this.$route.query.state);
let { data, error } = await fetch(u);
if (error) {
// set local login error on failed oauth2.
@ -44,29 +44,28 @@ export default {
return;
}
if (data.request_type === "loginuser") {
if (data.request_type === 'loginuser') {
setLoggedUser(data.response.token, data.response.user);
let redirect = getLoginRedirect(redirect);
if (redirect) {
unsetLoginRedirect();
this.$router.push(redirect);
} else {
this.$router.push({ name: "home" });
this.$router.push({ name: 'home' });
}
} else if (data.request_type === "authorize") {
this.$store.dispatch("setRegisterUser", data.response);
this.$router.push("/register");
} else if (data.request_type === "createuserla") {
} else if (data.request_type === 'authorize') {
this.$store.dispatch('setRegisterUser', data.response);
this.$router.push('/register');
} else if (data.request_type === 'createuserla') {
this.$router.push({
name: "user settings",
params: { username: this.username }
name: 'user settings',
params: { username: this.username },
});
}
}
},
},
created: function() {
created: function () {
this.doOauth2();
}
},
};
</script>

View File

@ -9,13 +9,15 @@
<span class="mx-2">/</span>
</li>
<li>
<router-link :to="ownerLink('org', orgname)">{{orgname}}</router-link>
<router-link :to="ownerLink('org', orgname)">{{
orgname
}}</router-link>
</li>
</ol>
</nav>
<div class="mb-8 flex justify-between">
<span class="text-3xl">{{orgname}}</span>
<span class="text-3xl">{{ orgname }}</span>
<createprojectbutton v-on:click="goToCreate($event)" />
</div>
@ -23,7 +25,12 @@
<ul class="flex-grow tab">
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name === 'org projects' || $route.name === 'org' }]"
:class="[
{
'tab-element-selected':
$route.name === 'org projects' || $route.name === 'org',
},
]"
>
<router-link :to="ownerProjectsLink('org', orgname)">
<i class="mr-1 mdi mdi-home" />
@ -42,7 +49,13 @@
<li
v-if="$route.name.endsWith('org project group settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('org project group settings') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith(
'org project group settings'
),
},
]"
>
<router-link :to="projectGroupSettingsLink('org', orgname, [])">
<i class="mr-1 mdi mdi-settings" />
@ -52,7 +65,9 @@
<li
v-if="$route.name.endsWith('org settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('org settings') }]"
:class="[
{ 'tab-element-selected': $route.name.endsWith('org settings') },
]"
>
<router-link :to="ownerSettingsLink('org', orgname)">
<i class="mr-1 mdi mdi-settings" />
@ -65,7 +80,7 @@
<div class="relative">
<div
class="flex -mt-3"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
>
<button
@ -108,9 +123,8 @@
</div>
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import {
ownerLink,
@ -119,23 +133,23 @@ import {
orgMembersLink,
projectGroupCreateProjectGroupLink,
projectGroupCreateProjectLink,
projectGroupSettingsLink
} from "@/util/link.js";
projectGroupSettingsLink,
} from '@/util/link.js';
import createprojectbutton from "@/components/createprojectbutton.vue";
import createprojectbutton from '@/components/createprojectbutton.vue';
export default {
name: "Org",
name: 'Org',
components: { createprojectbutton },
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
orgname: String
orgname: String,
},
data() {
return {
dropdownActive: false
dropdownActive: false,
};
},
methods: {
@ -147,19 +161,18 @@ export default {
projectGroupCreateProjectLink: projectGroupCreateProjectLink,
projectGroupSettingsLink: projectGroupSettingsLink,
goToCreate(type) {
if (type == "project") {
if (type == 'project') {
this.$router.push(
projectGroupCreateProjectLink("org", this.orgname, [])
projectGroupCreateProjectLink('org', this.orgname, [])
);
return;
}
this.$router.push(
projectGroupCreateProjectGroupLink("org", this.orgname, [])
projectGroupCreateProjectGroupLink('org', this.orgname, [])
);
}
}
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -1,9 +1,13 @@
<template>
<div>
<projbreadcrumbs :ownertype="ownertype" :ownername="ownername" :projectref="projectref" />
<projbreadcrumbs
:ownertype="ownertype"
:ownername="ownername"
:projectref="projectref"
/>
<div class="mb-8">
<span class="text-3xl">{{projectName()}}</span>
<span class="text-3xl">{{ projectName() }}</span>
</div>
<div class="flex justify-between">
@ -17,7 +21,13 @@
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project runs') || $route.name.endsWith('project') }]"
:class="[
{
'tab-element-selected':
$route.name.match('project runs') ||
$route.name.endsWith('project'),
},
]"
>
<router-link :to="projectRunsLink(ownertype, ownername, projectref)">
<i class="mr-1 mdi mdi-asterisk" />
@ -26,45 +36,84 @@
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project branches runs') }]"
:class="[
{
'tab-element-selected': $route.name.match(
'project branches runs'
),
},
]"
>
<router-link :to="projectBranchesRunsLink(ownertype, ownername, projectref)">
<router-link
:to="projectBranchesRunsLink(ownertype, ownername, projectref)"
>
<i class="mr-1 mdi mdi-source-branch" />
<span>Branches</span>
</router-link>
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project tags runs') }]"
:class="[
{ 'tab-element-selected': $route.name.match('project tags runs') },
]"
>
<router-link :to="projectTagsRunsLink(ownertype, ownername, projectref)">
<router-link
:to="projectTagsRunsLink(ownertype, ownername, projectref)"
>
<i class="mr-1 mdi mdi-tag" />
<span>Tags</span>
</router-link>
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project pull requests runs') }]"
:class="[
{
'tab-element-selected': $route.name.match(
'project pull requests runs'
),
},
]"
>
<router-link :to="projectPRsRunsLink(ownertype, ownername, projectref)">
<router-link
:to="projectPRsRunsLink(ownertype, ownername, projectref)"
>
<i class="mr-1 mdi mdi-source-pull" />
<span>Pull Requests</span>
</router-link>
</li>
<li
v-if="run && ($route.name.endsWith('project run') || $route.name.endsWith('project run task'))"
v-if="
run &&
($route.name.endsWith('project run') ||
$route.name.endsWith('project run task'))
"
>
<tabarrow />
</li>
<li
class="tab-element"
v-if="run && ($route.name.endsWith('project run') || $route.name.endsWith('project run task'))"
:class="[{ 'tab-element-selected': $route.name.endsWith('project run') }]"
v-if="
run &&
($route.name.endsWith('project run') ||
$route.name.endsWith('project run task'))
"
:class="[
{ 'tab-element-selected': $route.name.endsWith('project run') },
]"
>
<router-link :to="projectRunLink(ownertype, ownername, projectref, $route.params.runid)">
<router-link
:to="
projectRunLink(
ownertype,
ownername,
projectref,
$route.params.runid
)
"
>
<p>
Run
<strong>#{{run.counter}}</strong>
<strong>#{{ run.counter }}</strong>
</p>
</router-link>
</li>
@ -74,23 +123,41 @@
<li
class="tab-element"
v-if="run && $route.name.endsWith('project run task')"
:class="[{ 'tab-element-selected': $route.name.endsWith('project run task') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith('project run task'),
},
]"
>
<router-link
:to="projectRunTaskLink(ownertype, ownername, projectref, $route.params.runid, $route.params.taskid)"
:to="
projectRunTaskLink(
ownertype,
ownername,
projectref,
$route.params.runid,
$route.params.taskid
)
"
>
<p>
Task
<strong>{{run.tasks[$route.params.taskid].name}}</strong>
<strong>{{ run.tasks[$route.params.taskid].name }}</strong>
</p>
</router-link>
</li>
<li
v-if="$route.name.endsWith('project settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('project settings') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith('project settings'),
},
]"
>
<router-link :to="projectSettingsLink(ownertype, ownername, projectref)">
<router-link
:to="projectSettingsLink(ownertype, ownername, projectref)"
>
<i class="mr-1 mdi mdi-settings" />
<span>Project Settings</span>
</router-link>
@ -101,7 +168,7 @@
<div class="relative">
<div
class="flex -mt-3"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
>
<button
@ -135,9 +202,8 @@
</div>
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import {
projectLink,
@ -147,35 +213,35 @@ import {
projectPRsRunsLink,
projectRunLink,
projectRunTaskLink,
projectSettingsLink
} from "@/util/link.js";
projectSettingsLink,
} from '@/util/link.js';
import { fetchRun } from "@/util/data.js";
import { fetchRun } from '@/util/data.js';
import projbreadcrumbs from "@/components/projbreadcrumbs.vue";
import tabarrow from "@/components/tabarrow.vue";
import projbreadcrumbs from '@/components/projbreadcrumbs.vue';
import tabarrow from '@/components/tabarrow.vue';
export default {
name: "Project",
name: 'Project',
components: { projbreadcrumbs, tabarrow },
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
ownertype: String,
ownername: String,
projectref: Array
projectref: Array,
},
data() {
return {
fetchAbort: null,
dropdownActive: false,
run: null
run: null,
};
},
watch: {
$route: async function(route) {
$route: async function (route) {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
@ -191,12 +257,12 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.run = data;
}
}
},
},
methods: {
projectLink: projectLink,
@ -209,9 +275,9 @@ export default {
projectSettingsLink: projectSettingsLink,
projectName() {
return this.projectref[this.projectref.length - 1];
}
},
},
created: async function() {
created: async function () {
this.fetchAbort = new AbortController();
if (this.$route.params.runid) {
@ -223,7 +289,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.run = data;
@ -233,9 +299,8 @@ export default {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -1,4 +1,3 @@
<template>
<div>
<projbreadcrumbs
@ -8,7 +7,7 @@
/>
<div class="mb-8 flex justify-between">
<span class="text-3xl">{{projectGroupName()}}</span>
<span class="text-3xl">{{ projectGroupName() }}</span>
<createprojectbutton v-on:click="goToCreate($event)" />
</div>
@ -16,9 +15,19 @@
<ul class="flex-grow tab">
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project group project') || $route.name.endsWith('project group') }]"
:class="[
{
'tab-element-selected':
$route.name.match('project group project') ||
$route.name.endsWith('project group'),
},
]"
>
<router-link :to="projectGroupProjectsLink(ownertype, ownername, projectgroupref)">
<router-link
:to="
projectGroupProjectsLink(ownertype, ownername, projectgroupref)
"
>
<i class="mdi mdi-home" />
<span>Projects</span>
</router-link>
@ -26,9 +35,19 @@
<li
v-if="$route.name.endsWith('project group settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('project group settings') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith(
'project group settings'
),
},
]"
>
<router-link :to="projectGroupSettingsLink(ownertype, ownername, projectgroupref)">
<router-link
:to="
projectGroupSettingsLink(ownertype, ownername, projectgroupref)
"
>
<i class="mdi mdi-settings" />
<span>Project Group Settings</span>
</router-link>
@ -39,7 +58,7 @@
<div class="relative">
<div
class="flex -mt-3"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
>
<button
@ -57,7 +76,13 @@
<li>
<router-link
class="block px-4 py-2 hover:bg-blue-500 hover:text-white"
:to="projectGroupSettingsLink(ownertype, ownername, projectgroupref)"
:to="
projectGroupSettingsLink(
ownertype,
ownername,
projectgroupref
)
"
>
<i class="mdi mdi-settings" />
<span>Project Group Settings</span>
@ -73,34 +98,33 @@
</div>
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import {
projectGroupProjectsLink,
projectGroupSettingsLink,
projectGroupCreateProjectGroupLink,
projectGroupCreateProjectLink
} from "@/util/link.js";
projectGroupCreateProjectLink,
} from '@/util/link.js';
import projbreadcrumbs from "@/components/projbreadcrumbs.vue";
import createprojectbutton from "@/components/createprojectbutton.vue";
import projbreadcrumbs from '@/components/projbreadcrumbs.vue';
import createprojectbutton from '@/components/createprojectbutton.vue';
export default {
name: "ProjectGroup",
name: 'ProjectGroup',
components: { projbreadcrumbs, createprojectbutton },
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
dropdownActive: false
dropdownActive: false,
};
},
methods: {
@ -110,12 +134,12 @@ export default {
projectGroupCreateProjectLink: projectGroupCreateProjectLink,
projectGroupName() {
if (!this.projectgroupref.length) {
return "Root Project Group";
return 'Root Project Group';
}
return this.projectgroupref[this.projectgroupref.length - 1];
},
goToCreate(type) {
if (type == "project") {
if (type == 'project') {
this.$router.push(
projectGroupCreateProjectLink(
this.ownertype,
@ -132,10 +156,9 @@ export default {
this.projectgroupref
)
);
}
}
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -28,6 +28,9 @@
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
>
No remote sources defined
<router-link class="underline text-blue-600 block" to="/newsource">
<button class="btn btn-blue">Create one</button>
</router-link>
</div>
<div
v-else-if="!hasRegisterRemoteSources"
@ -67,29 +70,29 @@
</template>
<script>
import { mapGetters } from "vuex";
import { mapGetters } from 'vuex';
import LoginForm from "@/components/loginform";
import RegisterForm from "@/components/registerform";
import LoginForm from '@/components/loginform';
import RegisterForm from '@/components/registerform';
import { fetchRemoteSources, register } from "@/util/data";
import { fetchRemoteSources, register } from '@/util/data';
import { authorizeurl, fetch, doLogout } from "@/util/auth";
import { authorizeurl, fetch, doLogout } from '@/util/auth';
export default {
name: "Register",
name: 'Register',
components: {
LoginForm,
RegisterForm
RegisterForm,
},
data: function() {
data: function () {
return {
error: null,
remotesources: null
remotesources: null,
};
},
computed: {
...mapGetters(["registeruser"]),
...mapGetters(['registeruser']),
hasRemoteSources() {
if (this.remotesources) {
@ -104,13 +107,13 @@ export default {
}
}
return false;
}
},
},
methods: {
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.remotesources = data;
@ -119,12 +122,12 @@ export default {
let u = authorizeurl();
let res = await (
await fetch(u, {
method: "POST",
method: 'POST',
body: JSON.stringify({
remote_source_name: remotesourcename,
login_name: username,
password: password
})
password: password,
}),
})
).json();
@ -132,11 +135,11 @@ export default {
window.location = res.oauth2_redirect;
return;
}
this.$store.dispatch("setRegisterUser", {
this.$store.dispatch('setRegisterUser', {
remote_user_info: res.remote_user_info,
remote_source_name: res.remote_source_name,
remote_source_login_name: username,
remote_source_login_password: password
remote_source_login_password: password,
});
},
async doRegister(
@ -162,18 +165,15 @@ export default {
window.location = data.oauth2_redirect;
return;
}
this.$router.push({ name: "home" });
}
this.$router.push({ name: 'home' });
},
},
mounted: function() {
this.$store.dispatch("setError", null);
mounted: function () {
this.$store.dispatch('setError', null);
},
created: function() {
created: function () {
doLogout();
this.fetchRemoteSources();
}
},
};
</script>

View File

@ -9,13 +9,15 @@
<span class="mx-2">/</span>
</li>
<li>
<router-link :to="ownerLink('user', username)">{{username}}</router-link>
<router-link :to="ownerLink('user', username)">{{
username
}}</router-link>
</li>
</ol>
</nav>
<div class="mb-8 flex justify-between">
<span class="text-3xl">{{username}}</span>
<span class="text-3xl">{{ username }}</span>
<createprojectbutton v-on:click="goToCreate($event)" />
</div>
@ -23,7 +25,12 @@
<ul class="flex-grow tab">
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name === 'user projects' || $route.name === 'user' }]"
:class="[
{
'tab-element-selected':
$route.name === 'user projects' || $route.name === 'user',
},
]"
>
<router-link :to="ownerProjectsLink('user', username)">
<i class="mr-1 mdi mdi-home" />
@ -32,7 +39,9 @@
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name === 'user direct runs' }]"
:class="[
{ 'tab-element-selected': $route.name === 'user direct runs' },
]"
>
<router-link :to="userDirectRunsLink(username)">
<i class="mr-1 mdi mdi-run-fast" />
@ -40,19 +49,29 @@
</router-link>
</li>
<li
v-if="run && ($route.name === 'user direct run' || $route.name == 'user direct run task')"
v-if="
run &&
($route.name === 'user direct run' ||
$route.name == 'user direct run task')
"
>
<tabarrow />
</li>
<li
class="tab-element"
v-if="run && ($route.name === 'user direct run' || $route.name == 'user direct run task')"
:class="[{ 'tab-element-selected': $route.name === 'user direct run' }]"
v-if="
run &&
($route.name === 'user direct run' ||
$route.name == 'user direct run task')
"
:class="[
{ 'tab-element-selected': $route.name === 'user direct run' },
]"
>
<router-link :to="userDirectRunLink(username, $route.params.runid)">
<span>
Run
<strong>#{{run.counter}}</strong>
<strong>#{{ run.counter }}</strong>
</span>
</router-link>
</li>
@ -62,21 +81,35 @@
<li
class="tab-element"
v-if="run && $route.name == 'user direct run task'"
:class="[{ 'tab-element-selected': $route.name === 'user direct run task' }]"
:class="[
{ 'tab-element-selected': $route.name === 'user direct run task' },
]"
>
<router-link
:to="userDirectRunTaskLink(username, $route.params.runid, $route.params.taskid)"
:to="
userDirectRunTaskLink(
username,
$route.params.runid,
$route.params.taskid
)
"
>
<span>
Task
<strong>{{run.tasks[$route.params.taskid].name}}</strong>
<strong>{{ run.tasks[$route.params.taskid].name }}</strong>
</span>
</router-link>
</li>
<li
v-if="$route.name.endsWith('user project group settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('user project group settings') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith(
'user project group settings'
),
},
]"
>
<router-link :to="projectGroupSettingsLink('user', username, [])">
<i class="mr-1 mdi mdi-settings" />
@ -86,7 +119,9 @@
<li
v-if="$route.name.endsWith('user settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('user settings') }]"
:class="[
{ 'tab-element-selected': $route.name.endsWith('user settings') },
]"
>
<router-link :to="ownerSettingsLink('user', username)">
<i class="mr-1 mdi mdi-settings" />
@ -99,7 +134,7 @@
<div class="relative">
<div
class="flex -mt-3"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
>
<button
@ -134,7 +169,7 @@
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import {
ownerLink,
@ -145,33 +180,33 @@ import {
ownerSettingsLink,
projectGroupCreateProjectGroupLink,
projectGroupCreateProjectLink,
projectGroupSettingsLink
} from "@/util/link.js";
projectGroupSettingsLink,
} from '@/util/link.js';
import { fetchRun } from "@/util/data.js";
import { fetchRun } from '@/util/data.js';
import createprojectbutton from "@/components/createprojectbutton.vue";
import tabarrow from "@/components/tabarrow.vue";
import createprojectbutton from '@/components/createprojectbutton.vue';
import tabarrow from '@/components/tabarrow.vue';
export default {
name: "User",
name: 'User',
components: { createprojectbutton, tabarrow },
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
username: String
username: String,
},
data() {
return {
fetchAbort: null,
dropdownActive: false,
run: null
run: null,
};
},
watch: {
$route: async function(route) {
$route: async function (route) {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
@ -187,12 +222,12 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.run = data;
}
}
},
},
methods: {
ownerLink: ownerLink,
@ -205,18 +240,18 @@ export default {
projectGroupCreateProjectLink: projectGroupCreateProjectLink,
projectGroupSettingsLink: projectGroupSettingsLink,
goToCreate(type) {
if (type == "project") {
if (type == 'project') {
this.$router.push(
projectGroupCreateProjectLink("user", this.username, [])
projectGroupCreateProjectLink('user', this.username, [])
);
return;
}
this.$router.push(
projectGroupCreateProjectGroupLink("user", this.username, [])
projectGroupCreateProjectGroupLink('user', this.username, [])
);
}
},
},
created: async function() {
created: async function () {
this.fetchAbort = new AbortController();
if (this.$route.params.runid) {
@ -228,7 +263,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.run = data;
@ -238,9 +273,8 @@ export default {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -6,43 +6,54 @@ module.exports = {
padding: '2rem',
},
fontFamily: {
'sans': ['Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'Noto Sans', 'sans-serif', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji']
sans: [
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Arial',
'Noto Sans',
'sans-serif',
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
'Noto Color Emoji',
],
},
borderWidth: {
default: '1px',
'0': '0',
'2': '2px',
'4': '4px',
'5': '5px',
'6': '6px',
0: '0',
2: '2px',
4: '4px',
5: '5px',
6: '6px',
},
spacing: {
px: '1px',
'2px': '2px',
'3px': '3px',
'0': '0',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
'10': '2.5rem',
'12': '3rem',
'16': '4rem',
'20': '5rem',
'24': '6rem',
'32': '8rem',
'40': '10rem',
'48': '12rem',
'56': '14rem',
'64': '16rem',
0: '0',
1: '0.25rem',
2: '0.5rem',
3: '0.75rem',
4: '1rem',
5: '1.25rem',
6: '1.5rem',
8: '2rem',
10: '2.5rem',
12: '3rem',
16: '4rem',
20: '5rem',
24: '6rem',
32: '8rem',
40: '10rem',
48: '12rem',
56: '14rem',
64: '16rem',
},
colors: {
dark: '#4a4a4a',
}
}
},
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus', 'disabled'],
@ -56,9 +67,9 @@ module.exports = {
function ({ addVariant, e }) {
addVariant('disabled', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.${e(`disabled${separator}${className}`)}:disabled`
})
})
}
]
}
return `.${e(`disabled${separator}${className}`)}:disabled`;
});
});
},
],
};

View File

@ -1,7 +1,7 @@
const path = require("path");
const path = require('path');
module.exports = {
css: {
sourceMap: true
}
sourceMap: true,
},
};