Compare commits

...

11 Commits

Author SHA1 Message Date
silverwind
50bd7d0b24
Remove the service worker (#25010)
It's been disabled by default since 1.17
(https://github.com/go-gitea/gitea/pull/18914), and it never really
delivered any benefit except being another cache layer that has its own
unsolved invalidation issues. HTTP cache works, we don't need two cache
layers at the browser for assets.

## ⚠️ BREAKING

You can remove the config `[ui].USE_SERVICE_WORKER` from your `app.ini`
now.
2023-05-31 02:07:04 +00:00
HesterG
28a89e360f
Add user level action runners (#24995)
Used similar logic to organization.

<img width="1437" alt="Screen Shot 2023-05-30 at 10 18 06"
src="https://github.com/go-gitea/gitea/assets/17645053/49f3800a-44ae-4188-b1e6-91d49e3d7868">

<img width="1331" alt="Screen Shot 2023-05-30 at 10 31 18"
src="https://github.com/go-gitea/gitea/assets/17645053/221b2068-e9b9-4e34-bb4a-d390594b2f35">
2023-05-31 09:39:54 +08:00
6543
4c81dae297
Update github.com/google/go-github to v52 (#24004)
based on https://github.com/google/go-github/pull/2743

because of
https://github.com/go-gitea/gitea/pull/23946#discussion_r1160317554

---------

Co-authored-by: silverwind <me@silverwind.io>
2023-05-31 00:31:51 +00:00
Denys Konovalov
0c79a655d4
various style fixes (#25008)
- fixing various style issues (border color/radius, margin)
- added indent at some radio input blocks
---
### Before:
![Bildschirmfoto vom 2023-05-30
17-14-15](https://github.com/go-gitea/gitea/assets/47871822/59963646-d34f-4032-bd02-dbb48747b96d)

![Bildschirmfoto vom 2023-05-30
17-13-21](https://github.com/go-gitea/gitea/assets/47871822/87b67a35-8fbf-47ad-9903-499403dcebb5)

![Bildschirmfoto vom 2023-05-30
17-12-54](https://github.com/go-gitea/gitea/assets/47871822/6c92b427-ee54-42b1-b206-400e33d705ed)

![Bildschirmfoto vom 2023-05-30
17-05-29](https://github.com/go-gitea/gitea/assets/47871822/bd8d201d-bbc3-4c71-84fb-fb393d3c36bb)

![Bildschirmfoto vom 2023-05-30
17-04-36](https://github.com/go-gitea/gitea/assets/47871822/5f7a4e2a-f140-4162-8f3e-294356b42dee)

![Bildschirmfoto vom 2023-05-30
17-06-05](https://github.com/go-gitea/gitea/assets/47871822/d09e46b5-5e8f-41f1-af4b-24bc8cc12c21)

![Bildschirmfoto vom 2023-05-30
17-09-35](https://github.com/go-gitea/gitea/assets/47871822/179dd3f5-914d-4593-8698-46fdffd02d54)

### After:

![Bildschirmfoto vom 2023-05-30
17-14-03](https://github.com/go-gitea/gitea/assets/47871822/a404c53e-cd25-41bb-aece-bedf90c2e8ed)

![Bildschirmfoto vom 2023-05-30
17-13-35](https://github.com/go-gitea/gitea/assets/47871822/2ea8779c-9d13-44e7-8ad6-ebac10fac465)

![Bildschirmfoto vom 2023-05-30
17-12-45](https://github.com/go-gitea/gitea/assets/47871822/b34ffeb9-a7e8-4900-97ee-1894f91e189c)

![Bildschirmfoto vom 2023-05-30
17-05-18](https://github.com/go-gitea/gitea/assets/47871822/f8d18360-db3e-472a-9e86-575e6dcb4f36)

![Bildschirmfoto vom 2023-05-30
17-04-49](https://github.com/go-gitea/gitea/assets/47871822/12fc20d6-4fa5-4ac4-b788-a1506647ef47)

![Bildschirmfoto vom 2023-05-30
17-06-23](https://github.com/go-gitea/gitea/assets/47871822/2a82f8e9-3455-4619-9a2e-352c40c4e0b6)

![Bildschirmfoto vom 2023-05-30
17-11-26](https://github.com/go-gitea/gitea/assets/47871822/ab408878-45fb-4713-84c4-5777705a98ab)

---------

Co-authored-by: silverwind <me@silverwind.io>
2023-05-30 22:28:25 +00:00
HesterG
1ea5c8b0ff
Add show timestamp/seconds and fullscreen options to action page (#24876)
Part of #24728

- The timestamp shows local time and is parsed by `date.toLocaleString`;
- "show seconds" and "show timestamps" are mutually exclusive, and they
can be both hidden.


https://github.com/go-gitea/gitea/assets/17645053/89531e54-37b7-4400-a6a0-bb3cc69eb6f5

Update for timestamp format:

<img width="306" alt="Screen Shot 2023-05-25 at 09 07 47"
src="https://github.com/go-gitea/gitea/assets/17645053/2d99768d-d39c-4c9e-81a2-7bc7470399dd">

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2023-05-30 20:38:55 +00:00
John Olheiser
3dd3b1b456
Fix markdown link to awesome gitea (#25009)
Fixes the markdown link and uses title case like the other
translations.
2023-05-30 13:10:51 -05:00
JakobDev
1b115296d3
Followup to pinned Issues (#24945)
This addressees some things from #24406 that came up after the PR was
merged. Mostly from @delvh.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
2023-05-30 15:26:51 +00:00
Lunny Xiao
faae819f5d
revert the removed method to fix tmpl break on graph page (#25005)
Fix #24996 
Caused by #24634
2023-05-30 21:36:58 +08:00
wxiaoguang
ee99cf6313
Refactor diffFileInfo / DiffTreeStore (#24998)
Follow  #21012, #22399

Replace #24983, fix #24938

Help #24956

Now, the `window.config.pageData.diffFileInfo` itself is a reactive
store, so it's quite easy to sync values/states by it, no need to do
"doLoadMoreFiles" or "callback".

Screenshot: these two buttons both work. After complete loading, the UI
is also right.

<details>


![image](https://github.com/go-gitea/gitea/assets/2114189/cc6310fd-7f27-45ea-ab4f-24952a87b421)


![image](https://github.com/go-gitea/gitea/assets/2114189/4c11dd67-ac03-4568-8541-91204d27a4e3)


![image](https://github.com/go-gitea/gitea/assets/2114189/38a22cec-41be-41e6-a209-f347b7a4c1de)

</details>
2023-05-30 18:53:15 +08:00
HesterG
32185efc14
Fix delete user account modal (#25004)
Before:

<img width="953" alt="Screen Shot 2023-05-30 at 17 08 57"
src="https://github.com/go-gitea/gitea/assets/17645053/f379b2c9-7d9a-492e-b0e4-5a8be1c3a025">

After:

<img width="875" alt="Screen Shot 2023-05-30 at 17 06 17"
src="https://github.com/go-gitea/gitea/assets/17645053/75d3c9b5-201b-4001-a27e-64c932e2e34a">
2023-05-30 18:08:09 +08:00
silverwind
d783384c19
Clean up github actions (#24984)
- Merge the file filters into `files-changed.yml`
- Remove unused yaml anchors like `&backend`
- Merge the `compliance-docs` workflow into `compliance`
- Add actions linting
- Misc cleanups for whitespace and step names
2023-05-30 13:31:00 +08:00
57 changed files with 481 additions and 384 deletions

View File

@ -76,7 +76,6 @@ cpu.out
/yarn-error.log
/npm-debug.log*
/public/js
/public/serviceworker.js
/public/css
/public/fonts
/public/img/webpack

View File

@ -1,15 +0,0 @@
docs: &docs
- "**/*.md"
- "docs/**"
backend: &backend
- "**/*.go"
- "**/*.tmpl"
- "go.mod"
- "go.sum"
frontend: &frontend
- "**/*.js"
- "web_src/**"
- "package.json"
- "package-lock.json"

View File

@ -1,32 +1,53 @@
name: files changed
name: files-changed
on:
workflow_call:
outputs:
docs:
description: "whether docs files changed"
value: ${{ jobs.files-changed.outputs.docs }}
backend:
description: "whether backend files changed"
value: ${{ jobs.files-changed.outputs.backend }}
value: ${{ jobs.detect.outputs.backend }}
frontend:
description: "whether frontend files changed"
value: ${{ jobs.files-changed.outputs.frontend }}
value: ${{ jobs.detect.outputs.frontend }}
docs:
description: "whether docs files changed"
value: ${{ jobs.detect.outputs.docs }}
actions:
description: "whether actions files changed"
value: ${{ jobs.detect.outputs.actions }}
jobs:
files-changed:
detect:
name: detect which files changed
runs-on: ubuntu-latest
timeout-minutes: 3
# Map a step output to a job output
outputs:
docs: ${{ steps.changes.outputs.docs }}
backend: ${{ steps.changes.outputs.backend }}
frontend: ${{ steps.changes.outputs.frontend }}
docs: ${{ steps.changes.outputs.docs }}
actions: ${{ steps.changes.outputs.actions }}
steps:
- uses: actions/checkout@v3
- name: Check for backend file changes
uses: dorny/paths-filter@v2
- uses: dorny/paths-filter@v2
id: changes
with:
filters: .github/file-filters.yml
filters: |
backend:
- "**/*.go"
- "**/*.tmpl"
- "go.mod"
- "go.sum"
frontend:
- "**/*.js"
- "web_src/**"
- "package.json"
- "package-lock.json"
docs:
- "**/*.md"
- "docs/**"
actions:
- ".github/workflows/*"

View File

@ -1,25 +0,0 @@
name: compliance-docs
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
files-changed:
uses: ./.github/workflows/files-changed.yml
compliance-docs:
if: needs.files-changed.outputs.docs == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- run: make deps-frontend
- run: make lint-md
- run: make docs # test if build could succeed

View File

@ -25,6 +25,7 @@ jobs:
- run: make lint-backend
env:
TAGS: bindata sqlite sqlite_unlock_notify
lint-go-windows:
if: needs.files-changed.outputs.backend == 'true'
needs: files-changed
@ -41,6 +42,7 @@ jobs:
TAGS: bindata sqlite sqlite_unlock_notify
GOOS: windows
GOARCH: amd64
lint-go-gogit:
if: needs.files-changed.outputs.backend == 'true'
needs: files-changed
@ -55,6 +57,7 @@ jobs:
- run: make lint-go
env:
TAGS: bindata gogit sqlite sqlite_unlock_notify
checks-backend:
if: needs.files-changed.outputs.backend == 'true'
needs: files-changed
@ -67,6 +70,7 @@ jobs:
check-latest: true
- run: make deps-backend deps-tools
- run: make --always-make checks-backend # ensure the "go-licenses" make target runs
frontend:
if: needs.files-changed.outputs.frontend == 'true'
needs: files-changed
@ -79,6 +83,7 @@ jobs:
- run: make deps-frontend
- run: make lint-frontend
- run: make checks-frontend
backend:
if: needs.files-changed.outputs.backend == 'true'
needs: files-changed
@ -113,3 +118,25 @@ jobs:
env:
GOOS: linux
GOARCH: 386
docs:
if: needs.files-changed.outputs.docs == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- run: make deps-frontend
- run: make lint-md
- run: make docs # test if build could succeed
actions:
if: needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
- run: make lint-actions

1
.gitignore vendored
View File

@ -77,7 +77,6 @@ cpu.out
/yarn-error.log
/npm-debug.log*
/public/js
/public/serviceworker.js
/public/css
/public/fonts
/public/img/webpack

View File

@ -1,5 +1,6 @@
*.min.css
*.min.js
/assets/*.json
/modules/options/bindata.go
/modules/public/bindata.go
/modules/templates/bindata.go

View File

@ -114,7 +114,7 @@ FOMANTIC_WORK_DIR := web_src/fomantic
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
WEBPACK_CONFIGS := webpack.config.js
WEBPACK_DEST := public/js/index.js public/css/index.css
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/img/webpack public/serviceworker.js
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/img/webpack
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))

View File

@ -525,8 +525,8 @@
"licenseText": "Copyright (c) 2011 The Snappy-Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{
"name": "github.com/google/go-github/v51/github",
"path": "github.com/google/go-github/v51/github/LICENSE",
"name": "github.com/google/go-github/v52/github",
"path": "github.com/google/go-github/v52/github/LICENSE",
"licenseText": "Copyright (c) 2013 The go-github AUTHORS. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{

View File

@ -17,7 +17,7 @@ import (
"strings"
"syscall"
"github.com/google/go-github/v51/github"
"github.com/google/go-github/v52/github"
"github.com/urfave/cli"
"gopkg.in/yaml.v3"
)

View File

@ -1044,7 +1044,7 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; List of reasons why a Pull Request or Issue can be locked
;LOCK_REASONS = Too heated,Off-topic,Resolved,Spam
;; Maximum number of pinned Issues
;; Maximum number of pinned Issues per repo
;; Set to 0 to disable pinning Issues
;MAX_PINNED = 3
@ -1208,9 +1208,6 @@ LEVEL = Info
;; Whether to search within description at repository search on explore page.
;SEARCH_REPO_DESCRIPTION = true
;;
;; Whether to enable a Service Worker to cache frontend assets
;USE_SERVICE_WORKER = false
;;
;; Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used.
;; A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic).
;ONLY_SHOW_RELEVANT_REPOS = false

View File

@ -141,7 +141,7 @@ In addition there is _`StaticRootPath`_ which can be set as a built-in at build
### Repository - Issue (`repository.issue`)
- `LOCK_REASONS`: **Too heated,Off-topic,Resolved,Spam**: A list of reasons why a Pull Request or Issue can be locked
- `MAX_PINNED`: **3**: Maximum number of pinned Issues. Set to 0 to disable pinning Issues.
- `MAX_PINNED`: **3**: Maximum number of pinned Issues per Repo. Set to 0 to disable pinning Issues.
### Repository - Upload (`repository.upload`)
@ -230,7 +230,6 @@ The following configuration set `Content-Type: application/vnd.android.package-a
add it to this config.
- `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
- `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page.
- `USE_SERVICE_WORKER`: **false**: Whether to enable a Service Worker to cache frontend assets.
- `ONLY_SHOW_RELEVANT_REPOS`: **false** Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used.
A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic).

View File

@ -301,4 +301,4 @@ You can try it out using [the online demo](https://try.gitea.io/).
## Integrated support
Please visit [AWESOME GITEA] (https://gitea.com/gitea/awesome-gitea/) to get more third-party integrated support
Please visit [Awesome Gitea](https://gitea.com/gitea/awesome-gitea/) to get more third-party integrated support

8
go.mod
View File

@ -55,7 +55,7 @@ require (
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/go-github/v51 v51.0.0
github.com/google/go-github/v52 v52.0.0
github.com/google/pprof v0.0.0-20230502171905-255e3b9b56de
github.com/google/uuid v1.3.0
github.com/gorilla/feeds v1.1.1
@ -107,10 +107,10 @@ require (
github.com/yuin/goldmark v1.5.4
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87
github.com/yuin/goldmark-meta v1.1.0
golang.org/x/crypto v0.8.0
golang.org/x/crypto v0.9.0
golang.org/x/image v0.7.0
golang.org/x/net v0.10.0
golang.org/x/oauth2 v0.7.0
golang.org/x/oauth2 v0.8.0
golang.org/x/sys v0.8.0
golang.org/x/text v0.9.0
golang.org/x/tools v0.8.0
@ -136,7 +136,7 @@ require (
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230426101702-58e86b294756 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1 // indirect
github.com/RoaringBitmap/roaring v1.2.3 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect

18
go.sum
View File

@ -105,8 +105,8 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
github.com/ProtonMail/go-crypto v0.0.0-20230426101702-58e86b294756 h1:L6S7kR7SlhQKplIBpkra3s6yhcZV51lhRnXmYc4HohI=
github.com/ProtonMail/go-crypto v0.0.0-20230426101702-58e86b294756/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1 h1:JMDGhoQvXNTqH6Y3MC0IUw6tcZvaUdujNqzK2HYWZc8=
github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
@ -227,6 +227,7 @@ github.com/bufbuild/connect-go v1.7.0/go.mod h1:GmMJYR6orFqD0Y6ZgX8pwQ8j9baizDrI
github.com/buildkite/terminal-to-html/v3 v3.7.0 h1:chdLUSpiOj/A4v3dzxyOqixXI6aw7IDA6Dk77FXsvNU=
github.com/buildkite/terminal-to-html/v3 v3.7.0/go.mod h1:g0ME1XqbkBSgXR9YmlIHcJIjzaMyWW+HbsG0rPb5puo=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
@ -564,8 +565,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v51 v51.0.0 h1:KCjsbgPV28VoRftdP+K2mQL16jniUsLAJknsOVKwHyU=
github.com/google/go-github/v51 v51.0.0/go.mod h1:kZj/rn/c1lSUbr/PFWl2hhusPV7a5XNYKcwPrd5L3Us=
github.com/google/go-github/v52 v52.0.0 h1:uyGWOY+jMQ8GVGSX8dkSwCzlehU3WfdxQ7GweO/JP7M=
github.com/google/go-github/v52 v52.0.0/go.mod h1:WJV6VEEUPuMo5pXqqa2ZCZEdbQqua4zAk2MZTIo+m+4=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
@ -1318,10 +1319,11 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1430,8 +1432,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@ -69,7 +69,11 @@ func (r *ActionRunner) BelongsToOwnerType() types.OwnerType {
return types.OwnerTypeRepository
}
if r.OwnerID != 0 {
return types.OwnerTypeOrganization
if r.Owner.Type == user_model.UserTypeOrganization {
return types.OwnerTypeOrganization
} else if r.Owner.Type == user_model.UserTypeIndividual {
return types.OwnerTypeIndividual
}
}
return types.OwnerTypeSystemGlobal
}

View File

@ -687,6 +687,8 @@ func (issue *Issue) HasOriginalAuthor() bool {
return issue.OriginalAuthor != "" && issue.OriginalAuthorID != 0
}
var ErrIssueMaxPinReached = util.NewInvalidArgumentErrorf("the max number of pinned issues has been readched")
// IsPinned returns if a Issue is pinned
func (issue *Issue) IsPinned() bool {
return issue.PinOrder != 0
@ -707,7 +709,7 @@ func (issue *Issue) Pin(ctx context.Context, user *user_model.User) error {
// Check if the maximum allowed Pins reached
if maxPin >= setting.Repository.Issue.MaxPinned {
return fmt.Errorf("You have reached the max number of pinned Issues")
return ErrIssueMaxPinReached
}
_, err = db.GetEngine(ctx).Table("issue").
@ -856,10 +858,15 @@ func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) ([]*Issue,
// IsNewPinnedAllowed returns if a new Issue or Pull request can be pinned
func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) {
var maxPin int
_, err := db.GetEngine(ctx).SQL("SELECT MAX(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ?", repoID, isPull).Get(&maxPin)
_, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ? AND pin_order > 0", repoID, isPull).Get(&maxPin)
if err != nil {
return false, err
}
return maxPin < setting.Repository.Issue.MaxPinned, nil
}
// IsErrIssueMaxPinReached returns if the error is, that the User can't pin more Issues
func IsErrIssueMaxPinReached(err error) bool {
return err == ErrIssueMaxPinReached
}

View File

@ -53,6 +53,11 @@ func (ref *Reference) Commit() (*Commit, error) {
return ref.repo.getCommit(ref.Object)
}
// ShortName returns the short name of the reference
func (ref *Reference) ShortName() string {
return RefName(ref.Name).ShortName()
}
// RefGroup returns the group type of the reference
func (ref *Reference) RefGroup() string {
return RefName(ref.Name).RefGroup()

View File

@ -32,7 +32,6 @@ var UI = struct {
CustomEmojis []string
CustomEmojisMap map[string]string `ini:"-"`
SearchRepoDescription bool
UseServiceWorker bool
OnlyShowRelevantRepos bool
Notification struct {
@ -136,7 +135,6 @@ func loadUIFrom(rootCfg ConfigProvider) {
UI.ShowUserEmail = sec.Key("SHOW_USER_EMAIL").MustBool(true)
UI.DefaultShowFullName = sec.Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
UI.SearchRepoDescription = sec.Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
UI.UseServiceWorker = sec.Key("USE_SERVICE_WORKER").MustBool(false)
// OnlyShowRelevantRepos=false is important for many private/enterprise instances,
// because many private repositories do not have "description/topic", users just want to search by their names.

View File

@ -127,9 +127,6 @@ func NewFuncMap() template.FuncMap {
"MetaKeywords": func() string {
return setting.UI.Meta.Keywords
},
"UseServiceWorker": func() bool {
return setting.UI.UseServiceWorker
},
"EnableTimetracking": func() bool {
return setting.Service.EnableTimetracking
},

View File

@ -125,6 +125,10 @@ concept_user_individual = Individual
concept_code_repository = Repository
concept_user_organization = Organization
show_timestamps = Show timestamps
show_log_seconds = Show seconds
show_full_screen = Show full screen
[aria]
navbar = Navigation Bar
footer = Footer

23
package-lock.json generated
View File

@ -51,8 +51,6 @@
"vue3-calendar-heatmap": "2.0.5",
"webpack": "5.84.1",
"webpack-cli": "5.1.1",
"workbox-routing": "6.6.0",
"workbox-strategies": "6.6.0",
"wrap-ansi": "8.1.0"
},
"devDependencies": {
@ -11045,27 +11043,6 @@
"node": ">=0.10.0"
}
},
"node_modules/workbox-core": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.6.0.tgz",
"integrity": "sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ=="
},
"node_modules/workbox-routing": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.6.0.tgz",
"integrity": "sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==",
"dependencies": {
"workbox-core": "6.6.0"
}
},
"node_modules/workbox-strategies": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.6.0.tgz",
"integrity": "sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==",
"dependencies": {
"workbox-core": "6.6.0"
}
},
"node_modules/wrap-ansi": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",

View File

@ -51,8 +51,6 @@
"vue3-calendar-heatmap": "2.0.5",
"webpack": "5.84.1",
"webpack-cli": "5.1.1",
"workbox-routing": "6.6.0",
"workbox-strategies": "6.6.0",
"wrap-ansi": "8.1.0"
},
"devDependencies": {

View File

@ -45,6 +45,8 @@ func PinIssue(ctx *context.APIContext) {
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
ctx.NotFound()
} else if issues_model.IsErrIssueMaxPinReached(err) {
ctx.Error(http.StatusBadRequest, "MaxPinReached", err)
} else {
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
}
@ -55,11 +57,13 @@ func PinIssue(ctx *context.APIContext) {
err = issue.LoadRepo(ctx)
if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadRepo", err)
return
}
err = issue.Pin(ctx, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "PinIssue", err)
return
}
ctx.Status(http.StatusNoContent)
@ -108,11 +112,13 @@ func UnpinIssue(ctx *context.APIContext) {
err = issue.LoadRepo(ctx)
if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadRepo", err)
return
}
err = issue.Unpin(ctx, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "UnpinIssue", err)
return
}
ctx.Status(http.StatusNoContent)
@ -166,6 +172,7 @@ func MoveIssuePin(ctx *context.APIContext) {
err = issue.MovePin(ctx, int(ctx.ParamsInt64(":position")))
if err != nil {
ctx.Error(http.StatusInternalServerError, "MovePin", err)
return
}
ctx.Status(http.StatusNoContent)
@ -193,12 +200,12 @@ func ListPinnedIssues(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/IssueList"
issues, err := issues_model.GetPinnedIssues(ctx, ctx.Repo.Repository.ID, false)
if err == nil {
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
} else {
if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadPinnedIssues", err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
}
// ListPinnedPullRequests returns a list of all pinned PRs
@ -225,6 +232,7 @@ func ListPinnedPullRequests(ctx *context.APIContext) {
issues, err := issues_model.GetPinnedIssues(ctx, ctx.Repo.Repository.ID, true)
if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadPinnedPullRequests", err)
return
}
apiPrs := make([]*api.PullRequest, len(issues))

View File

@ -9,6 +9,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
)
// IssuePinOrUnpin pin or unpin a Issue
@ -19,12 +20,14 @@ func IssuePinOrUnpin(ctx *context.Context) {
err := issue.LoadRepo(ctx)
if err != nil {
ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return
}
err = issue.PinOrUnpin(ctx, ctx.Doer)
if err != nil {
ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return
}
@ -33,9 +36,10 @@ func IssuePinOrUnpin(ctx *context.Context) {
// IssueUnpin unpins a Issue
func IssueUnpin(ctx *context.Context) {
issue, err := issues_model.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
issue, err := issues_model.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
ctx.Status(http.StatusNoContent)
ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return
}
@ -43,12 +47,15 @@ func IssueUnpin(ctx *context.Context) {
err = issue.LoadRepo(ctx)
if err != nil {
ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return
}
err = issue.Unpin(ctx, ctx.Doer)
if err != nil {
ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return
}
ctx.Status(http.StatusNoContent)
@ -69,18 +76,21 @@ func IssuePinMove(ctx *context.Context) {
form := &movePinIssueForm{}
if err := json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return
}
issue, err := issues_model.GetIssueByID(ctx, form.ID)
if err != nil {
ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return
}
err = issue.MovePin(ctx, form.Position)
if err != nil {
ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return
}

View File

@ -21,9 +21,11 @@ const (
tplRepoRunners base.TplName = "repo/settings/actions"
tplOrgRunners base.TplName = "org/settings/actions"
tplAdminRunners base.TplName = "admin/actions"
tplUserRunners base.TplName = "user/settings/actions"
tplRepoRunnerEdit base.TplName = "repo/settings/runner_edit"
tplOrgRunnerEdit base.TplName = "org/settings/runners_edit"
tplAdminRunnerEdit base.TplName = "admin/runners/edit"
tplUserRunnerEdit base.TplName = "user/settings/runner_edit"
)
type runnersCtx struct {
@ -32,6 +34,7 @@ type runnersCtx struct {
IsRepo bool
IsOrg bool
IsAdmin bool
IsUser bool
RunnersTemplate base.TplName
RunnerEditTemplate base.TplName
RedirectLink string
@ -71,6 +74,17 @@ func getRunnersCtx(ctx *context.Context) (*runnersCtx, error) {
}, nil
}
if ctx.Data["PageIsUserSettings"] == true {
return &runnersCtx{
OwnerID: ctx.Doer.ID,
RepoID: 0,
IsUser: true,
RunnersTemplate: tplUserRunners,
RunnerEditTemplate: tplUserRunnerEdit,
RedirectLink: setting.AppSubURL + "/user/settings/actions/runners/",
}, nil
}
return nil, errors.New("unable to set Runners context")
}
@ -102,7 +116,7 @@ func Runners(ctx *context.Context) {
if rCtx.IsRepo {
opts.RepoID = rCtx.RepoID
opts.WithAvailable = true
} else if rCtx.IsOrg {
} else if rCtx.IsOrg || rCtx.IsUser {
opts.OwnerID = rCtx.OwnerID
opts.WithAvailable = true
}

View File

@ -9,5 +9,5 @@ import (
)
func RedirectToDefaultSetting(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/actions/secrets")
ctx.Redirect(setting.AppSubURL + "/user/settings/actions/runners")
}

View File

@ -492,6 +492,7 @@ func registerRoutes(m *web.Route) {
m.Group("/actions", func() {
m.Get("", user_setting.RedirectToDefaultSetting)
addSettingsRunnersRoutes()
addSettingsSecretsRoutes()
}, actions.MustEnableActions)
@ -1025,8 +1026,8 @@ func registerRoutes(m *web.Route) {
m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation)
m.Post("/attachments", repo.UploadIssueAttachment)
m.Post("/attachments/remove", repo.DeleteAttachment)
m.Delete("/unpin/{id}", reqRepoAdmin, repo.IssueUnpin)
m.Post("/pin_move", reqRepoAdmin, repo.IssuePinMove)
m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin)
m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove)
}, context.RepoMustNotBeArchived())
m.Group("/comments/{id}", func() {
m.Post("", repo.UpdateCommentContent)

View File

@ -7,7 +7,7 @@ package migrations
import (
"errors"
"github.com/google/go-github/v51/github"
"github.com/google/go-github/v52/github"
)
// ErrRepoNotCreated returns the error that repository not created

View File

@ -20,7 +20,7 @@ import (
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/structs"
"github.com/google/go-github/v51/github"
"github.com/google/go-github/v52/github"
"golang.org/x/oauth2"
)
@ -256,11 +256,11 @@ func (g *GithubDownloaderV3) GetMilestones() ([]*base.Milestone, error) {
milestones = append(milestones, &base.Milestone{
Title: m.GetTitle(),
Description: m.GetDescription(),
Deadline: convertGithubTimestampToTime(m.DueOn),
Deadline: m.DueOn.GetTime(),
State: state,
Created: m.GetCreatedAt().Time,
Updated: convertGithubTimestampToTime(m.UpdatedAt),
Closed: convertGithubTimestampToTime(m.ClosedAt),
Updated: m.UpdatedAt.GetTime(),
Closed: m.ClosedAt.GetTime(),
})
}
if len(ms) < perPage {
@ -715,11 +715,11 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq
State: pr.GetState(),
Created: pr.GetCreatedAt().Time,
Updated: pr.GetUpdatedAt().Time,
Closed: convertGithubTimestampToTime(pr.ClosedAt),
Closed: pr.ClosedAt.GetTime(),
Labels: labels,
Merged: pr.MergedAt != nil,
MergeCommitSHA: pr.GetMergeCommitSHA(),
MergedTime: convertGithubTimestampToTime(pr.MergedAt),
MergedTime: pr.MergedAt.GetTime(),
IsLocked: pr.ActiveLockReason != nil,
Head: base.PullRequestBranch{
Ref: pr.GetHead().GetRef(),
@ -878,10 +878,3 @@ func (g *GithubDownloaderV3) GetReviews(reviewable base.Reviewable) ([]*base.Rev
}
return allReviews, nil
}
func convertGithubTimestampToTime(t *github.Timestamp) *time.Time {
if t == nil {
return nil
}
return &t.Time
}

View File

@ -5,7 +5,7 @@
</h4>
<div class="ui attached table segment">
<form method="post" action="{{AppSubUrl}}/admin">
<table class="ui very basic striped table unstackable">
<table class="ui very basic striped table unstackable gt-mb-0">
<thead>
<tr>
<th></th>

View File

@ -19,7 +19,7 @@
<div class="ui attached table segment">
<form method="post" action="{{AppSubUrl}}/admin">
{{.CsrfTokenHtml}}
<table class="ui very basic table gt-px-4">
<table class="ui very basic table gt-mt-0 gt-px-4">
<tbody>
<tr>
<td>{{.locale.Tr "admin.dashboard.delete_inactive_accounts"}}</td>

View File

@ -166,7 +166,7 @@
<label>{{.locale.Tr "settings.lookup_avatar_by_mail"}}</label>
</div>
</div>
<div class="field {{if .Err_Gravatar}}error{{end}}">
<div class="field gt-pl-4 {{if .Err_Gravatar}}error{{end}}">
<label for="gravatar">Avatar {{.locale.Tr "email"}}</label>
<input id="gravatar" name="gravatar" value="{{.User.AvatarEmail}}">
</div>
@ -179,7 +179,7 @@
</div>
</div>
<div class="inline field">
<div class="inline field gt-pl-4">
<label for="avatar">{{.locale.Tr "settings.choose_new_avatar"}}</label>
<input name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
</div>
@ -197,17 +197,17 @@
{{svg "octicon-trash"}}
{{.locale.Tr "settings.delete_account_title"}}
</div>
<div class="content">
<p>{{.locale.Tr "settings.delete_account_desc"}}</p>
</div>
<form class="ui form" method="POST" action="{{.Link}}/delete">
{{$.CsrfTokenHtml}}
<div class="field">
<div class="ui checkbox">
<label for="purge">{{.locale.Tr "admin.users.purge"}}</label>
<input name="purge" type="checkbox">
<div class="content">
<p>{{.locale.Tr "settings.delete_account_desc"}}</p>
{{$.CsrfTokenHtml}}
<div class="field">
<div class="ui checkbox">
<label for="purge">{{.locale.Tr "admin.users.purge"}}</label>
<input name="purge" type="checkbox">
</div>
<p class="help">{{.locale.Tr "admin.users.purge_help"}}</p>
</div>
<p class="help">{{.locale.Tr "admin.users.purge_help"}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</form>

View File

@ -12,7 +12,6 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly.
assetUrlPrefix: '{{AssetUrlPrefix}}',
runModeIsProd: {{.RunModeIsProd}},
customEmojis: {{CustomEmojis}},
useServiceWorker: {{UseServiceWorker}},
csrfToken: '{{.CsrfToken}}',
pageData: {{.PageData}},
notificationSettings: {{NotificationSettings}}, {{/*a map provided by NewFuncMap in helper.go*/}}

View File

@ -19,6 +19,9 @@
data-locale-status-skipped="{{.locale.Tr "actions.status.skipped"}}"
data-locale-status-blocked="{{.locale.Tr "actions.status.blocked"}}"
data-locale-artifacts-title="{{$.locale.Tr "artifacts"}}"
data-locale-show-timestamps="{{.locale.Tr "show_timestamps"}}"
data-locale-show-log-seconds="{{.locale.Tr "show_log_seconds"}}"
data-locale-show-full-screen="{{.locale.Tr "show_full_screen"}}"
>
</div>
</div>

View File

@ -45,33 +45,31 @@
{{end}}
</div>
</div>
<script id="diff-data-script">
(() => {
const diffData = {
files: [{{range $i, $file := .Diff.Files}}{Name:"{{$file.Name}}",NameHash:"{{$file.NameHash}}",Type:{{$file.Type}},IsBin:{{$file.IsBin}},Addition:{{$file.Addition}},Deletion:{{$file.Deletion}}},{{end}}],
isIncomplete: {{.Diff.IsIncomplete}},
tooManyFilesMessage: "{{$.locale.Tr "repo.diff.too_many_files"}}",
binaryFileMessage: "{{$.locale.Tr "repo.diff.bin"}}",
showMoreMessage: "{{.locale.Tr "repo.diff.show_more"}}",
statisticsMessage: "{{.locale.Tr "repo.diff.stats_desc_file"}}",
fileTreeIsVisible: false,
fileListIsVisible: false,
isLoadingNewData: false,
diffEnd: {{.Diff.End}},
link: "{{$.Link}}"
};
if(window.config.pageData.diffFileInfo) {
// Page is already loaded - add the data to our existing data
window.config.pageData.diffFileInfo.files.push(...diffData.files);
window.config.pageData.diffFileInfo.isIncomplete = diffData.isIncomplete;
window.config.pageData.diffFileInfo.diffEnd = diffData.diffEnd;
window.config.pageData.diffFileInfo.link = diffData.link;
} else {
// new load of page - populate initial data
window.config.pageData.diffFileInfo = diffData;
}
})();
</script>
<script id="diff-data-script" type="module">
const diffDataFiles = [{{range $i, $file := .Diff.Files}}{Name:"{{$file.Name}}",NameHash:"{{$file.NameHash}}",Type:{{$file.Type}},IsBin:{{$file.IsBin}},Addition:{{$file.Addition}},Deletion:{{$file.Deletion}}},{{end}}];
const diffData = {
isIncomplete: {{.Diff.IsIncomplete}},
tooManyFilesMessage: "{{$.locale.Tr "repo.diff.too_many_files"}}",
binaryFileMessage: "{{$.locale.Tr "repo.diff.bin"}}",
showMoreMessage: "{{.locale.Tr "repo.diff.show_more"}}",
statisticsMessage: "{{.locale.Tr "repo.diff.stats_desc_file"}}",
linkLoadMore: "{{$.Link}}?skip-to={{.Diff.End}}&file-only=true",
};
// for first time loading, the diffFileInfo is a plain object
// after the Vue component is mounted, the diffFileInfo is a reactive object
// keep in mind that this script block would be executed many times when loading more files, by "loadMoreFiles"
let diffFileInfo = window.config.pageData.diffFileInfo || {
files:[],
fileTreeIsVisible: false,
fileListIsVisible: false,
isLoadingNewData: false,
selectedItem: '',
};
diffFileInfo = Object.assign(diffFileInfo, diffData);
diffFileInfo.files.push(...diffDataFiles);
window.config.pageData.diffFileInfo = diffFileInfo;
</script>
<div id="diff-file-list"></div>
<div id="diff-container">
<div id="diff-file-tree" class="gt-hidden"></div>

View File

@ -6,7 +6,7 @@
{{if .PinnedIssues}}
<div id="issue-pins" {{if .IsRepoAdmin}}data-is-repo-admin{{end}}>
{{range .PinnedIssues}}
<div class="pinned-issue-card gt-word-break" data-move-url="{{$.Link}}/pin_move" data-issue-id="{{.ID}}">
<div class="pinned-issue-card gt-word-break" data-move-url="{{$.Link}}/move_pin" data-issue-id="{{.ID}}">
{{if eq $.Project.CardType 1}}
<div class="card-attachment-images">
{{range (index $.issuesAttachmentMap .ID)}}
@ -21,7 +21,7 @@
</div>
<a class="pinned-issue-title muted issue-title" href="{{.Link}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
{{if $.IsRepoAdmin}}
<a role="button" class="pinned-issue-unpin muted gt-df gt-ac" data-tooltip-content={{$.locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Link}}/unpin/{{.ID}}">
<a role="button" class="pinned-issue-unpin muted gt-df gt-ac" data-tooltip-content={{$.locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Link}}/unpin/{{.Index}}">
{{svg "octicon-x" 16}}
</a>
{{end}}

View File

@ -330,7 +330,7 @@
<label>{{.locale.Tr "repo.settings.use_external_wiki"}}</label>
</div>
</div>
<div class="field {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}disabled{{end}}" id="external_wiki_box">
<div class="field gt-pl-4 {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}disabled{{end}}" id="external_wiki_box">
<label for="external_wiki_url">{{.locale.Tr "repo.settings.external_wiki_url"}}</label>
<input id="external_wiki_url" name="external_wiki_url" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}">
<p class="help">{{.locale.Tr "repo.settings.external_wiki_url_desc"}}</p>
@ -362,7 +362,7 @@
<label>{{.locale.Tr "repo.settings.use_internal_issue_tracker"}}</label>
</div>
</div>
<div class="field {{if (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="internal_issue_box">
<div class="field gt-pl-4 {{if (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="internal_issue_box">
{{if .Repository.CanEnableTimetracker}}
<div class="field">
<div class="ui checkbox">
@ -398,7 +398,7 @@
<label>{{.locale.Tr "repo.settings.use_external_issue_tracker"}}</label>
</div>
</div>
<div class="field {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="external_issue_box">
<div class="field gt-pl-4 {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="external_issue_box">
<div class="field">
<label for="external_tracker_url">{{.locale.Tr "repo.settings.external_tracker_url"}}</label>
<input id="external_tracker_url" name="external_tracker_url" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerURL}}">

View File

@ -2,6 +2,8 @@
<div class="user-setting-content">
{{if eq .PageType "secrets"}}
{{template "shared/secrets/add_list" .}}
{{else if eq .PageType "runners"}}
{{template "shared/actions/runner_list" .}}
{{end}}
</div>

View File

@ -23,6 +23,9 @@
<div class="item">
{{.locale.Tr "actions.actions"}}
<div class="menu">
<a class="{{if .PageIsSharedSettingsRunners}}active {{end}}item" href="{{AppSubUrl}}/user/settings/actions/runners">
{{.locale.Tr "actions.runners"}}
</a>
<a class="{{if .PageIsSharedSettingsSecrets}}active {{end}}item" href="{{AppSubUrl}}/user/settings/actions/secrets">
{{.locale.Tr "secrets.secrets"}}
</a>

View File

@ -105,7 +105,7 @@
<label>{{.locale.Tr "settings.lookup_avatar_by_mail"}}</label>
</div>
</div>
<div class="field {{if .Err_Gravatar}}error{{end}}">
<div class="field gt-pl-4 {{if .Err_Gravatar}}error{{end}}">
<label for="gravatar">Avatar {{.locale.Tr "email"}}</label>
<input id="gravatar" name="gravatar" value="{{.SignedUser.AvatarEmail}}">
</div>
@ -118,7 +118,7 @@
</div>
</div>
<div class="inline field">
<div class="inline field gt-pl-4">
<label for="avatar">{{.locale.Tr "settings.choose_new_avatar"}}</label>
<input name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
</div>

View File

@ -0,0 +1,5 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings runners")}}
<div class="user-setting-content">
{{template "shared/actions/runner_edit" .}}
</div>
{{template "user/settings/layout_footer" .}}

View File

@ -74,12 +74,15 @@
--color-secondary-alpha-90: #dededee1;
--color-secondary-hover: var(--color-secondary-dark-1);
--color-secondary-active: var(--color-secondary-dark-2);
/* console colors */
--color-console-fg: #ffffff;
--color-console-bg: #252a2f;
--color-console-border: #ffffff16;
/* console colors - used for actions console and console files */
--color-console-fg: #eeeff2;
--color-console-fg-subtle: #959cab;
--color-console-bg: #262936;
--color-console-border: #383c47;
--color-console-hover-bg: #ffffff16;
--color-console-active-bg: #353a3f;
--color-console-active-bg: #454a57;
--color-console-menu-bg: #383c47;
--color-console-menu-border: #5c6374;
/* named colors */
--color-red: #db2828;
--color-orange: #f2711c;
@ -553,7 +556,7 @@ a.label,
color: var(--color-text-light-3);
}
.ui.menu .item::before {
.ui.menu .item::before, .ui.vertical.menu .item::before {
background: var(--color-secondary);
}
@ -603,6 +606,7 @@ a.label,
.ui.dropdown .menu .active.item {
color: var(--color-text);
background: var(--color-active);
border-radius: 0;
font-weight: var(--font-weight-normal);
}
@ -847,6 +851,7 @@ a.label,
.ui.table > thead > tr > th {
background: var(--color-box-header);
border-color: var(--color-secondary);
color: var(--color-text);
}

View File

@ -3419,14 +3419,6 @@ tbody.commit-list {
background: var(--color-card);
}
.pinned-issue-card .meta a {
color: inherit;
}
.pinned-issue-card .meta a:hover {
color: var(--color-primary);
}
.pinned-issue-icon,
.pinned-issue-unpin {
margin-top: 1px;

View File

@ -60,12 +60,15 @@
--color-secondary-alpha-90: #525767e1;
--color-secondary-hover: var(--color-secondary-light-1);
--color-secondary-active: var(--color-secondary-light-2);
/* console colors */
--color-console-fg: #ffffff;
/* console colors - used for actions console and console files */
--color-console-fg: #eeeff2;
--color-console-fg-subtle: #959cab;
--color-console-bg: #262936;
--color-console-border: #ffffff16;
--color-console-border: #383c47;
--color-console-hover-bg: #ffffff16;
--color-console-active-bg: #383c47;
--color-console-active-bg: #454a57;
--color-console-menu-bg: #383c47;
--color-console-menu-border: #5c6374;
/* named colors */
--color-red: #cc4848;
--color-orange: #cc580c;

View File

@ -1,10 +1,10 @@
<template>
<ol class="diff-detail-box diff-stats gt-m-0" ref="root" v-if="fileListIsVisible">
<li v-for="file in files" :key="file.NameHash">
<ol class="diff-detail-box diff-stats gt-m-0" ref="root" v-if="store.fileListIsVisible">
<li v-for="file in store.files" :key="file.NameHash">
<div class="gt-font-semibold gt-df gt-ac pull-right">
<span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ binaryFileMessage }}</span>
<span v-if="file.IsBin" class="gt-ml-1 gt-mr-3">{{ store.binaryFileMessage }}</span>
{{ file.IsBin ? '' : file.Addition + file.Deletion }}
<span v-if="!file.IsBin" class="diff-stats-bar gt-mx-3" :data-tooltip-content="statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)">
<span v-if="!file.IsBin" class="diff-stats-bar gt-mx-3" :data-tooltip-content="store.statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)">
<div class="diff-stats-add-bar" :style="{ 'width': diffStatsWidth(file.Addition, file.Deletion) }"/>
</span>
</div>
@ -12,22 +12,21 @@
<span :class="['status', diffTypeToString(file.Type)]" :data-tooltip-content="diffTypeToString(file.Type)">&nbsp;</span>
<a class="file gt-mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a>
</li>
<li v-if="isIncomplete" id="diff-too-many-files-stats" class="gt-pt-2">
<span class="file gt-df gt-ac gt-sb">{{ tooManyFilesMessage }}
<a :class="['ui', 'basic', 'tiny', 'button', isLoadingNewData === true ? 'disabled' : '']" id="diff-show-more-files-stats" @click.stop="loadMoreData">{{ showMoreMessage }}</a>
<li v-if="store.isIncomplete" class="gt-pt-2">
<span class="file gt-df gt-ac gt-sb">{{ store.tooManyFilesMessage }}
<a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
</span>
</li>
</ol>
</template>
<script>
import {doLoadMoreFiles} from '../features/repo-diff.js';
const {pageData} = window.config;
import {loadMoreFiles} from '../features/repo-diff.js';
import {diffTreeStore} from '../modules/stores.js';
export default {
data: () => {
return pageData.diffFileInfo;
return {store: diffTreeStore()};
},
mounted() {
document.getElementById('show-file-list-btn').addEventListener('click', this.toggleFileList);
@ -37,7 +36,7 @@ export default {
},
methods: {
toggleFileList() {
this.fileListIsVisible = !this.fileListIsVisible;
this.store.fileListIsVisible = !this.store.fileListIsVisible;
},
diffTypeToString(pType) {
const diffTypes = {
@ -53,10 +52,7 @@ export default {
return `${adds / (adds + dels) * 100}%`;
},
loadMoreData() {
this.isLoadingNewData = true;
doLoadMoreFiles(this.link, this.diffEnd, () => {
this.isLoadingNewData = false;
});
loadMoreFiles(this.store.linkLoadMore);
}
},
};

View File

@ -1,42 +1,33 @@
<template>
<div
v-if="fileTreeIsVisible"
class="gt-mr-3 gt-mt-3 diff-detail-box"
>
<div v-if="store.fileTreeIsVisible" class="gt-mr-3 gt-mt-3 diff-detail-box">
<!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
<div class="ui list">
<DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/>
</div>
<div v-if="isIncomplete" id="diff-too-many-files-stats" class="gt-pt-2">
<span class="gt-mr-2">{{ tooManyFilesMessage }}</span><a :class="['ui', 'basic', 'tiny', 'button', isLoadingNewData === true ? 'disabled' : '']" id="diff-show-more-files-stats" @click.stop="loadMoreData">{{ showMoreMessage }}</a>
<div v-if="store.isIncomplete" class="gt-pt-2">
<a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
</div>
</div>
</template>
<script>
import DiffFileTreeItem from './DiffFileTreeItem.vue';
import {doLoadMoreFiles} from '../features/repo-diff.js';
import {loadMoreFiles} from '../features/repo-diff.js';
import {toggleElem} from '../utils/dom.js';
import {DiffTreeStore} from '../modules/stores.js';
import {diffTreeStore} from '../modules/stores.js';
import {setFileFolding} from '../features/file-fold.js';
const {pageData} = window.config;
const LOCAL_STORAGE_KEY = 'diff_file_tree_visible';
export default {
components: {DiffFileTreeItem},
data: () => {
const fileTreeIsVisible = localStorage.getItem(LOCAL_STORAGE_KEY) === 'true';
pageData.diffFileInfo.fileTreeIsVisible = fileTreeIsVisible;
return {
...pageData.diffFileInfo,
store: DiffTreeStore,
};
return {store: diffTreeStore()};
},
computed: {
fileTree() {
const result = [];
for (const file of this.files) {
for (const file of this.store.files) {
// Split file into directories
const splits = file.Name.split('/');
let index = 0;
@ -98,9 +89,7 @@ export default {
}
},
mounted() {
// replace the pageData.diffFileInfo.files with our watched data so we get updates
pageData.diffFileInfo.files = this.files;
this.store.fileTreeIsVisible = localStorage.getItem(LOCAL_STORAGE_KEY) === 'true';
document.querySelector('.diff-toggle-file-tree-button').addEventListener('click', this.toggleVisibility);
this.hashChangeListener = () => {
@ -124,12 +113,12 @@ export default {
}
},
toggleVisibility() {
this.updateVisibility(!this.fileTreeIsVisible);
this.updateVisibility(!this.store.fileTreeIsVisible);
},
updateVisibility(visible) {
this.fileTreeIsVisible = visible;
localStorage.setItem(LOCAL_STORAGE_KEY, this.fileTreeIsVisible);
this.updateState(this.fileTreeIsVisible);
this.store.fileTreeIsVisible = visible;
localStorage.setItem(LOCAL_STORAGE_KEY, this.store.fileTreeIsVisible);
this.updateState(this.store.fileTreeIsVisible);
},
updateState(visible) {
const btn = document.querySelector('.diff-toggle-file-tree-button');
@ -142,12 +131,7 @@ export default {
toggleElem(toHide, visible);
},
loadMoreData() {
this.isLoadingNewData = true;
doLoadMoreFiles(this.link, this.diffEnd, () => {
this.isLoadingNewData = false;
const {pageData} = window.config;
this.diffEnd = pageData.diffFileInfo.diffEnd;
});
loadMoreFiles(this.store.linkLoadMore);
},
},
};

View File

@ -40,7 +40,7 @@
<script>
import {SvgIcon} from '../svg.js';
import {DiffTreeStore} from '../modules/stores.js';
import {diffTreeStore} from '../modules/stores.js';
export default {
components: {SvgIcon},
@ -56,7 +56,7 @@ export default {
},
},
data: () => ({
store: DiffTreeStore,
store: diffTreeStore(),
collapsed: false,
}),
methods: {

View File

@ -60,14 +60,38 @@
<div class="action-view-right">
<div class="job-info-header">
<h3 class="job-info-header-title">
{{ currentJob.title }}
</h3>
<p class="job-info-header-detail">
{{ currentJob.detail }}
</p>
<div class="job-info-header-left">
<h3 class="job-info-header-title">
{{ currentJob.title }}
</h3>
<p class="job-info-header-detail">
{{ currentJob.detail }}
</p>
</div>
<div class="job-info-header-right">
<div class="ui top right pointing dropdown custom jump item" @click.stop="menuVisible = !menuVisible" @keyup.enter="menuVisible = !menuVisible">
<button class="ui button button-ghost gt-p-3">
<SvgIcon name="octicon-gear" :size="18"/>
</button>
<div class="menu transition action-job-menu" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak>
<a class="item" @click="toggleTimeDisplay('seconds')">
<i class="icon"><SvgIcon v-show="timeVisible['log-time-seconds']" name="octicon-check"/></i>
{{ locale.showLogSeconds }}
</a>
<a class="item" @click="toggleTimeDisplay('stamp')">
<i class="icon"><SvgIcon v-show="timeVisible['log-time-stamp']" name="octicon-check"/></i>
{{ locale.showTimeStamps }}
</a>
<div class="divider"/>
<a class="item" @click="toggleFullScreen()">
<i class="icon"><SvgIcon v-show="isFullScreen" name="octicon-check"/></i>
{{ locale.showFullScreen }}
</a>
</div>
</div>
</div>
</div>
<div class="job-step-container">
<div class="job-step-container" ref="steps">
<div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i">
<div class="job-step-summary" @click.stop="toggleStepLogs(i)" :class="currentJobStepsStates[i].expanded ? 'selected' : ''">
<!-- If the job is done and the job step log is loaded for the first time, show the loading icon
@ -81,7 +105,8 @@
<span class="step-summary-duration">{{ jobStep.duration }}</span>
</div>
<!-- the log elements could be a lot, do not use v-if to destroy/reconstruct the DOM -->
<!-- the log elements could be a lot, do not use v-if to destroy/reconstruct the DOM,
use native DOM elements for "log line" to improve performance, Vue is not suitable for managing so many reactive elements. -->
<div class="job-step-logs" ref="logs" v-show="currentJobStepsStates[i].expanded"/>
</div>
</div>
@ -95,6 +120,8 @@ import {SvgIcon} from '../svg.js';
import ActionRunStatus from './ActionRunStatus.vue';
import {createApp} from 'vue';
import AnsiToHTML from 'ansi-to-html';
import {toggleElem} from '../utils/dom.js';
import {getCurrentLocale} from '../utils.js';
const {csrfToken} = window.config;
@ -121,6 +148,12 @@ const sfc = {
currentJobStepsStates: [],
artifacts: [],
onHoverRerunIndex: -1,
menuVisible: false,
isFullScreen: false,
timeVisible: {
'log-time-stamp': false,
'log-time-seconds': false,
},
// provided by backend
run: {
@ -173,6 +206,11 @@ const sfc = {
// load job data and then auto-reload periodically
this.loadJob();
this.intervalID = setInterval(this.loadJob, 1000);
document.body.addEventListener('click', this.closeDropdown);
},
beforeUnmount() {
document.body.removeEventListener('click', this.closeDropdown);
},
unmounted() {
@ -240,7 +278,7 @@ const sfc = {
this.fetchPost(`${this.run.link}/approve`);
},
createLogLine(line) {
createLogLine(line, startTime) {
const div = document.createElement('div');
div.classList.add('job-log-line');
div._jobLogTime = line.timestamp;
@ -250,21 +288,35 @@ const sfc = {
lineNumber.textContent = line.index;
div.append(lineNumber);
// TODO: Support displaying time optionally
// for "Show timestamps"
const logTimeStamp = document.createElement('span');
logTimeStamp.className = 'log-time-stamp';
const date = new Date(parseFloat(line.timestamp * 1000));
const timeStamp = date.toLocaleString(getCurrentLocale(), {timeZoneName: 'short'});
logTimeStamp.textContent = timeStamp;
toggleElem(logTimeStamp, this.timeVisible['log-time-stamp']);
// for "Show seconds"
const logTimeSeconds = document.createElement('span');
logTimeSeconds.className = 'log-time-seconds';
const seconds = Math.floor(parseFloat(line.timestamp) - parseFloat(startTime));
logTimeSeconds.textContent = `${seconds}s`;
toggleElem(logTimeSeconds, this.timeVisible['log-time-seconds']);
const logMessage = document.createElement('div');
const logMessage = document.createElement('span');
logMessage.className = 'log-msg';
logMessage.innerHTML = ansiLogToHTML(line.message);
div.append(logTimeStamp);
div.append(logMessage);
div.append(logTimeSeconds);
return div;
},
appendLogs(stepIndex, logLines) {
appendLogs(stepIndex, logLines, startTime) {
for (const line of logLines) {
// TODO: group support: ##[group]GroupTitle , ##[endgroup]
const el = this.getLogsContainer(stepIndex);
el.append(this.createLogLine(line));
el.append(this.createLogLine(line, startTime));
}
},
@ -309,7 +361,7 @@ const sfc = {
for (const logs of response.logs.stepsLog) {
// save the cursor, it will be passed to backend next time
this.currentJobStepsStates[logs.step].cursor = logs.cursor;
this.appendLogs(logs.step, logs.lines);
this.appendLogs(logs.step, logs.lines, logs.started);
}
if (this.run.done && this.intervalID) {
@ -335,6 +387,46 @@ const sfc = {
isDone(status) {
return ['success', 'skipped', 'failure', 'cancelled'].includes(status);
},
closeDropdown() {
if (this.menuVisible) this.menuVisible = false;
},
// show at most one of log seconds and timestamp (can be both invisible)
toggleTimeDisplay(type) {
const toToggleTypes = [];
const other = type === 'seconds' ? 'stamp' : 'seconds';
this.timeVisible[`log-time-${type}`] = !this.timeVisible[`log-time-${type}`];
toToggleTypes.push(type);
if (this.timeVisible[`log-time-${type}`] && this.timeVisible[`log-time-${other}`]) {
this.timeVisible[`log-time-${other}`] = false;
toToggleTypes.push(other);
}
for (const toToggle of toToggleTypes) {
for (const el of this.$refs.steps.querySelectorAll(`.log-time-${toToggle}`)) {
toggleElem(el, this.timeVisible[`log-time-${toToggle}`]);
}
}
},
toggleFullScreen() {
this.isFullScreen = !this.isFullScreen;
const fullScreenEl = document.querySelector('.action-view-right');
const outerEl = document.querySelector('.full.height');
const actionBodyEl = document.querySelector('.action-view-body');
const headerEl = document.querySelector('.ui.main.menu');
const contentEl = document.querySelector('.page-content.repository');
const footerEl = document.querySelector('.page-footer');
toggleElem(headerEl, !this.isFullScreen);
toggleElem(contentEl, !this.isFullScreen);
toggleElem(footerEl, !this.isFullScreen);
// move .action-view-right to new parent
if (this.isFullScreen) {
outerEl.append(fullScreenEl);
} else {
actionBodyEl.append(fullScreenEl);
}
}
},
};
@ -360,6 +452,9 @@ export function initRepositoryActionView() {
rerun: el.getAttribute('data-locale-rerun'),
artifactsTitle: el.getAttribute('data-locale-artifacts-title'),
rerun_all: el.getAttribute('data-locale-rerun-all'),
showTimeStamps: el.getAttribute('data-locale-show-timestamps'),
showLogSeconds: el.getAttribute('data-locale-show-log-seconds'),
showFullScreen: el.getAttribute('data-locale-show-full-screen'),
status: {
unknown: el.getAttribute('data-locale-status-unknown'),
waiting: el.getAttribute('data-locale-status-waiting'),
@ -369,7 +464,7 @@ export function initRepositoryActionView() {
cancelled: el.getAttribute('data-locale-status-cancelled'),
skipped: el.getAttribute('data-locale-status-skipped'),
blocked: el.getAttribute('data-locale-status-blocked'),
}
},
}
});
view.mount(el);
@ -567,21 +662,95 @@ export function ansiLogToHTML(line) {
.action-view-right {
flex: 1;
color: var(--color-secondary-dark-3);
color: var(--color-console-fg-subtle);
max-height: 100%;
width: 70%;
display: flex;
flex-direction: column;
}
/* begin fomantic button overrides */
.action-view-right .ui.button,
.action-view-right .ui.button:focus {
background: transparent;
color: var(--color-console-fg-subtle);
}
.action-view-right .ui.button:hover {
background: var(--color-console-hover-bg);
color: var(--color-console-fg);
}
.action-view-right .ui.button:active {
background: var(--color-console-active-bg);
color: var(--color-console-fg);
}
/* end fomantic button overrides */
/* begin fomantic dropdown menu overrides */
.action-view-right .ui.dropdown .menu {
background: var(--color-console-menu-bg);
border-color: var(--color-console-menu-border);
}
.action-view-right .ui.dropdown .menu > .item {
color: var(--color-console-fg);
}
.action-view-right .ui.dropdown .menu > .item:hover {
color: var(--color-console-fg);
background: var(--color-console-hover-bg);
}
.action-view-right .ui.dropdown .menu > .item:active {
color: var(--color-console-fg);
background: var(--color-console-active-bg);
}
.action-view-right .ui.dropdown .menu > .divider {
border-top-color: var(--color-console-menu-border);
}
.action-view-right .ui.pointing.dropdown > .menu:not(.hidden)::after {
background: var(--color-console-menu-bg);
box-shadow: -1px -1px 0 0 var(--color-console-menu-border);
}
/* end fomantic dropdown menu overrides */
/* selectors here are intentionally exact to only match fullscreen */
.full.height > .action-view-right {
width: 100%;
height: 100%;
padding: 0;
border-radius: 0;
}
.full.height > .action-view-right > .job-info-header {
border-radius: 0;
}
.full.height > .action-view-right > .job-step-container {
height: calc(100% - 60px);
border-radius: 0;
}
.job-info-header {
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 12px;
border-bottom: 1px solid var(--color-console-border);
background-color: var(--color-console-bg);
position: sticky;
top: 0;
border-radius: var(--border-radius) var(--border-radius) 0 0;
height: 60px;
z-index: 1;
}
.job-info-header .job-info-header-title {
@ -591,7 +760,7 @@ export function ansiLogToHTML(line) {
}
.job-info-header .job-info-header-detail {
color: var(--color-secondary-dark-3);
color: var(--color-console-fg-subtle);
font-size: 12px;
}
@ -676,14 +845,20 @@ export function ansiLogToHTML(line) {
background-color: var(--color-console-hover-bg);
}
.job-step-section .job-step-logs .job-log-line .line-num {
/* class names 'log-time-seconds' and 'log-time-stamp' are used in the method toggleTimeDisplay */
.job-log-line .line-num, .log-time-seconds {
width: 48px;
color: var(--color-grey-light);
text-align: right;
user-select: none;
}
.job-step-section .job-step-logs .job-log-line .log-time {
.log-time-seconds {
padding-right: 2px;
}
.job-log-line .log-time,
.log-time-stamp {
color: var(--color-grey-light);
margin-left: 10px;
white-space: nowrap;

View File

@ -5,7 +5,7 @@ import {initDiffFileTree} from './repo-diff-filetree.js';
import {validateTextareaNonEmpty} from './comp/ComboMarkdownEditor.js';
import {initViewedCheckboxListenerFor, countAndUpdateViewedFiles, initExpandAndCollapseFilesButton} from './pull-view-file.js';
const {csrfToken} = window.config;
const {csrfToken, pageData} = window.config;
function initRepoDiffReviewButton() {
const $reviewBox = $('#review-box');
@ -119,37 +119,29 @@ function onShowMoreFiles() {
countAndUpdateViewedFiles();
}
export function doLoadMoreFiles(link, diffEnd, callback) {
const url = `${link}?skip-to=${diffEnd}&file-only=true`;
loadMoreFiles(url, callback);
}
function loadMoreFiles(url, callback) {
export function loadMoreFiles(url) {
const $target = $('a#diff-show-more-files');
if ($target.hasClass('disabled')) {
callback();
if ($target.hasClass('disabled') || pageData.diffFileInfo.isLoadingNewData) {
return;
}
pageData.diffFileInfo.isLoadingNewData = true;
$target.addClass('disabled');
$.ajax({
type: 'GET',
url,
}).done((resp) => {
if (!resp) {
$target.removeClass('disabled');
callback(resp);
return;
}
$('#diff-incomplete').replaceWith($(resp).find('#diff-file-boxes').children());
// By simply rerunning the script we add the new data to our existing
// pagedata object. this triggers vue and the filetree and filelist will
// render the new elements.
$('body').append($(resp).find('script#diff-data-script'));
const $resp = $(resp);
// the response is a full HTML page, we need to extract the relevant contents:
// 1. append the newly loaded file list items to the existing list
$('#diff-incomplete').replaceWith($resp.find('#diff-file-boxes').children());
// 2. re-execute the script to append the newly loaded items to the JS variables to refresh the DiffFileTree
$('body').append($resp.find('script#diff-data-script'));
onShowMoreFiles();
callback(resp);
}).fail(() => {
}).always(() => {
$target.removeClass('disabled');
callback();
pageData.diffFileInfo.isLoadingNewData = false;
});
}
@ -158,7 +150,8 @@ function initRepoDiffShowMore() {
e.preventDefault();
const $target = $(e.target);
loadMoreFiles($target.data('href'), () => {});
const linkLoadMore = $target.attr('data-href');
loadMoreFiles(linkLoadMore);
});
$(document).on('click', 'a.diff-load-button', (e) => {

View File

@ -1,57 +0,0 @@
import {joinPaths, parseUrl} from '../utils.js';
const {useServiceWorker, assetUrlPrefix, assetVersionEncoded} = window.config;
const cachePrefix = 'static-cache-v'; // actual version is set in the service worker script
const workerUrl = `${joinPaths(assetUrlPrefix, 'serviceworker.js')}?v=${assetVersionEncoded}`;
async function unregisterAll() {
for (const registration of await navigator.serviceWorker.getRegistrations()) {
if (registration.active) await registration.unregister();
}
}
async function unregisterOtherWorkers() {
for (const registration of await navigator.serviceWorker.getRegistrations()) {
const scriptPath = parseUrl(registration.active?.scriptURL || '').pathname;
const workerPath = parseUrl(workerUrl).pathname;
if (scriptPath !== workerPath) await registration.unregister();
}
}
async function invalidateCache() {
for (const key of await caches.keys()) {
if (key.startsWith(cachePrefix)) caches.delete(key);
}
}
async function checkCacheValidity() {
const cacheKey = assetVersionEncoded;
const storedCacheKey = localStorage.getItem('staticCacheKey');
// invalidate cache if it belongs to a different gitea version
if (cacheKey && storedCacheKey !== cacheKey) {
await invalidateCache();
localStorage.setItem('staticCacheKey', cacheKey);
}
}
export async function initServiceWorker() {
if (!('serviceWorker' in navigator)) return;
if (useServiceWorker) {
// unregister all service workers where scriptURL does not match the current one
await unregisterOtherWorkers();
try {
// the spec strictly requires it to be same-origin so the AssetUrlPrefix should contain AppSubUrl
await checkCacheValidity();
await navigator.serviceWorker.register(workerUrl);
} catch (err) {
console.error(err);
await invalidateCache();
await unregisterAll();
}
} else {
await invalidateCache();
await unregisterAll();
}
}

View File

@ -11,7 +11,6 @@ import {initHeatmap} from './features/heatmap.js';
import {initImageDiff} from './features/imagediff.js';
import {initRepoMigration} from './features/repo-migration.js';
import {initRepoProject} from './features/repo-projects.js';
import {initServiceWorker} from './features/serviceworker.js';
import {initTableSort} from './features/tablesort.js';
import {initAdminUserListSearchForm} from './features/admin/users.js';
import {initAdminConfigs} from './features/admin/config.js';
@ -116,7 +115,6 @@ onDomReady(() => {
initImageDiff();
initMarkupAnchors();
initMarkupContent();
initServiceWorker();
initSshKeyFormParser();
initStopwatch();
initTableSort();

View File

@ -1,5 +1,10 @@
import {reactive} from 'vue';
export const DiffTreeStore = reactive({
selectedItem: '',
});
let diffTreeStoreReactive;
export function diffTreeStore() {
if (!diffTreeStoreReactive) {
diffTreeStoreReactive = reactive(window.config.pageData.diffFileInfo);
window.config.pageData.diffFileInfo = diffTreeStoreReactive;
}
return diffTreeStoreReactive;
}

View File

@ -1,23 +0,0 @@
import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
const cacheName = 'static-cache-v2';
// disable workbox debug logging in development, remove when debugging the service worker
self.__WB_DISABLE_DEV_LOGS = true;
// see https://developer.mozilla.org/en-US/docs/Web/API/RequestDestination for possible values
const cachedDestinations = new Set([
'font',
'manifest',
'paintworklet',
'script',
'sharedworker',
'style',
'worker',
]);
registerRoute(
({request}) => cachedDestinations.has(request.destination),
new StaleWhileRevalidate({cacheName}),
);

View File

@ -26,6 +26,7 @@ import octiconEye from '../../public/img/svg/octicon-eye.svg';
import octiconFile from '../../public/img/svg/octicon-file.svg';
import octiconFileDirectoryFill from '../../public/img/svg/octicon-file-directory-fill.svg';
import octiconFilter from '../../public/img/svg/octicon-filter.svg';
import octiconGear from '../../public/img/svg/octicon-gear.svg';
import octiconGitBranch from '../../public/img/svg/octicon-git-branch.svg';
import octiconGitMerge from '../../public/img/svg/octicon-git-merge.svg';
import octiconGitPullRequest from '../../public/img/svg/octicon-git-pull-request.svg';
@ -94,6 +95,7 @@ const svgs = {
'octicon-file': octiconFile,
'octicon-file-directory-fill': octiconFileDirectoryFill,
'octicon-filter': octiconFilter,
'octicon-gear': octiconGear,
'octicon-git-branch': octiconGitBranch,
'octicon-git-merge': octiconGitMerge,
'octicon-git-pull-request': octiconGitPullRequest,
@ -132,7 +134,7 @@ const svgs = {
'octicon-tag': octiconTag,
'octicon-triangle-down': octiconTriangleDown,
'octicon-x': octiconX,
'octicon-x-circle-fill': octiconXCircleFill
'octicon-x-circle-fill': octiconXCircleFill,
};
// TODO: use a more general approach to access SVG icons.

View File

@ -60,7 +60,7 @@ export function parseUrl(str) {
}
// return current locale chosen by user
function getCurrentLocale() {
export function getCurrentLocale() {
return document.documentElement.lang;
}

View File

@ -62,9 +62,6 @@ export default {
fileURLToPath(new URL('web_src/js/standalone/swagger.js', import.meta.url)),
fileURLToPath(new URL('web_src/css/standalone/swagger.css', import.meta.url)),
],
serviceworker: [
fileURLToPath(new URL('web_src/js/serviceworker.js', import.meta.url)),
],
'eventsource.sharedworker': [
fileURLToPath(new URL('web_src/js/features/eventsource.sharedworker.js', import.meta.url)),
],
@ -73,11 +70,7 @@ export default {
devtool: false,
output: {
path: fileURLToPath(new URL('public', import.meta.url)),
filename: ({chunk}) => {
// serviceworker can only manage assets below it's script's directory so
// we have to put it in / instead of /js/
return chunk.name === 'serviceworker' ? '[name].js' : 'js/[name].js';
},
filename: () => 'js/[name].js',
chunkFilename: ({chunk}) => {
const language = (/monaco.*languages?_.+?_(.+?)_/.exec(chunk.id) || [])[1];
return `js/${language ? `monaco-language-${language.toLowerCase()}` : `[name]`}.[contenthash:8].js`;