mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-21 00:01:16 -04:00
Compare commits
No commits in common. "21f1e223d81ee98645abee141e58019d8141dcc9" and "f19feb0f473f33cae336cf7771b801e13641885e" have entirely different histories.
21f1e223d8
...
f19feb0f47
428
.drone.yml
Normal file
428
.drone.yml
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: release-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
base: /source
|
||||||
|
path: /
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: deps-frontend
|
||||||
|
image: node:20
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- make deps-frontend
|
||||||
|
|
||||||
|
- name: deps-backend
|
||||||
|
image: gitea/test_env:linux-1.20-amd64
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- make deps-backend
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: static
|
||||||
|
image: techknowlogick/xgo:go-1.21.x
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- apt-get update && apt-get -qqy install ca-certificates curl gnupg
|
||||||
|
- mkdir -p /etc/apt/keyrings && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||||
|
- echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" > /etc/apt/sources.list.d/nodesource.list
|
||||||
|
- apt-get update && apt-get -qqy install nodejs
|
||||||
|
- export PATH=$PATH:$GOPATH/bin
|
||||||
|
- make release
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
depends_on: [fetch-tags]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: gpg-sign
|
||||||
|
image: plugins/gpgsign:1
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
detach_sign: true
|
||||||
|
excludes:
|
||||||
|
- "dist/release/*.sha256"
|
||||||
|
files:
|
||||||
|
- "dist/release/*"
|
||||||
|
environment:
|
||||||
|
GPGSIGN_KEY:
|
||||||
|
from_secret: gpgsign_key
|
||||||
|
GPGSIGN_PASSPHRASE:
|
||||||
|
from_secret: gpgsign_passphrase
|
||||||
|
depends_on: [static]
|
||||||
|
|
||||||
|
- name: release-tag
|
||||||
|
image: woodpeckerci/plugin-s3:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
acl:
|
||||||
|
from_secret: aws_s3_acl
|
||||||
|
region:
|
||||||
|
from_secret: aws_s3_region
|
||||||
|
bucket:
|
||||||
|
from_secret: aws_s3_bucket
|
||||||
|
endpoint:
|
||||||
|
from_secret: aws_s3_endpoint
|
||||||
|
path_style:
|
||||||
|
from_secret: aws_s3_path_style
|
||||||
|
source: "dist/release/*"
|
||||||
|
strip_prefix: dist/release/
|
||||||
|
target: "/gitea/${DRONE_TAG##v}"
|
||||||
|
environment:
|
||||||
|
AWS_ACCESS_KEY_ID:
|
||||||
|
from_secret: aws_access_key_id
|
||||||
|
AWS_SECRET_ACCESS_KEY:
|
||||||
|
from_secret: aws_secret_access_key
|
||||||
|
depends_on: [gpg-sign]
|
||||||
|
|
||||||
|
- name: github
|
||||||
|
image: plugins/github-release:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
files:
|
||||||
|
- "dist/release/*"
|
||||||
|
file_exists: overwrite
|
||||||
|
environment:
|
||||||
|
GITHUB_TOKEN:
|
||||||
|
from_secret: github_token
|
||||||
|
depends_on: [gpg-sign]
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-linux-amd64-release-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
ref:
|
||||||
|
include:
|
||||||
|
- "refs/tags/**"
|
||||||
|
exclude:
|
||||||
|
- "refs/tags/**-rc*"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: publish
|
||||||
|
image: plugins/docker:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-amd64
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: publish-rootless
|
||||||
|
image: plugins/docker:latest
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-amd64-rootless
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-linux-amd64-release-candidate-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
ref:
|
||||||
|
- "refs/tags/**-rc*"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: publish
|
||||||
|
image: plugins/docker:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
tags: ${DRONE_TAG##v}-linux-amd64
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: publish-rootless
|
||||||
|
image: plugins/docker:latest
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
tags: ${DRONE_TAG##v}-linux-amd64-rootless
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-linux-arm64-release-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
ref:
|
||||||
|
include:
|
||||||
|
- "refs/tags/**"
|
||||||
|
exclude:
|
||||||
|
- "refs/tags/**-rc*"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: publish
|
||||||
|
image: plugins/docker:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-arm64
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: publish-rootless
|
||||||
|
image: plugins/docker:latest
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-arm64-rootless
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-linux-arm64-release-candidate-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
ref:
|
||||||
|
- "refs/tags/**-rc*"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: publish
|
||||||
|
image: plugins/docker:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
tags: ${DRONE_TAG##v}-linux-arm64
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: publish-rootless
|
||||||
|
image: plugins/docker:latest
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
tags: ${DRONE_TAG##v}-linux-arm64-rootless
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-manifest-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: manifest-rootless
|
||||||
|
image: plugins/manifest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
auto_tag: true
|
||||||
|
ignore_missing: true
|
||||||
|
spec: docker/manifest.rootless.tmpl
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
|
||||||
|
- name: manifest
|
||||||
|
image: plugins/manifest
|
||||||
|
settings:
|
||||||
|
auto_tag: true
|
||||||
|
ignore_missing: true
|
||||||
|
spec: docker/manifest.tmpl
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
ref:
|
||||||
|
- "refs/tags/**"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- docker-linux-amd64-release-version
|
||||||
|
- docker-linux-amd64-release-candidate-version
|
||||||
|
- docker-linux-arm64-release-version
|
||||||
|
- docker-linux-arm64-release-candidate-version
|
2
.github/workflows/release-nightly.yml
vendored
2
.github/workflows/release-nightly.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: release-nightly
|
name: release-nightly-assets
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
125
.github/workflows/release-tag-rc.yml
vendored
125
.github/workflows/release-tag-rc.yml
vendored
@ -1,125 +0,0 @@
|
|||||||
name: release-tag-rc
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v1*-rc*'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
binary:
|
|
||||||
runs-on: nscloud
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version: "~1.21"
|
|
||||||
check-latest: true
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
- run: make deps-frontend deps-backend
|
|
||||||
# xgo build
|
|
||||||
- run: make release
|
|
||||||
env:
|
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
|
||||||
- name: import gpg key
|
|
||||||
id: import_gpg
|
|
||||||
uses: crazy-max/ghaction-import-gpg@v5
|
|
||||||
with:
|
|
||||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
|
||||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
|
||||||
- name: sign binaries
|
|
||||||
run: |
|
|
||||||
for f in dist/release/*; do
|
|
||||||
echo '${{ secrets.GPGSIGN_PASSPHRASE }}' | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u ${{ steps.import_gpg.outputs.fingerprint }} --output "$f.asc" "$f"
|
|
||||||
done
|
|
||||||
# clean branch name to get the folder name in S3
|
|
||||||
- name: Get cleaned branch name
|
|
||||||
id: clean_name
|
|
||||||
run: |
|
|
||||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
|
||||||
echo "Cleaned name is ${REF_NAME}"
|
|
||||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
|
||||||
- name: configure aws
|
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
|
||||||
aws-region: ${{ secrets.AWS_REGION }}
|
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
- name: upload binaries to s3
|
|
||||||
run: |
|
|
||||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
|
||||||
- name: create github release
|
|
||||||
run: |
|
|
||||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
||||||
docker-rootful:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: docker/setup-qemu-action@v2
|
|
||||||
- uses: docker/setup-buildx-action@v2
|
|
||||||
- uses: docker/metadata-action@v5
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: gitea/gitea
|
|
||||||
# 1.2.3-rc0
|
|
||||||
tags: |
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: build rootful docker image
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
docker-rootless:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: docker/setup-qemu-action@v2
|
|
||||||
- uses: docker/setup-buildx-action@v2
|
|
||||||
- uses: docker/metadata-action@v5
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: gitea/gitea
|
|
||||||
# each tag below will have the suffix of -rootless
|
|
||||||
flavor: |
|
|
||||||
suffix=-rootless
|
|
||||||
# 1.2.3-rc0
|
|
||||||
tags: |
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: build rootless docker image
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
file: Dockerfile.rootless
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
141
.github/workflows/release-tag-version.yml
vendored
141
.github/workflows/release-tag-version.yml
vendored
@ -1,141 +0,0 @@
|
|||||||
name: release-tag-version
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v1.*'
|
|
||||||
- '!v1*-rc*'
|
|
||||||
- '!v1*-dev'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
binary:
|
|
||||||
runs-on: nscloud
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version: "~1.21"
|
|
||||||
check-latest: true
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
- run: make deps-frontend deps-backend
|
|
||||||
# xgo build
|
|
||||||
- run: make release
|
|
||||||
env:
|
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
|
||||||
- name: import gpg key
|
|
||||||
id: import_gpg
|
|
||||||
uses: crazy-max/ghaction-import-gpg@v5
|
|
||||||
with:
|
|
||||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
|
||||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
|
||||||
- name: sign binaries
|
|
||||||
run: |
|
|
||||||
for f in dist/release/*; do
|
|
||||||
echo '${{ secrets.GPGSIGN_PASSPHRASE }}' | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u ${{ steps.import_gpg.outputs.fingerprint }} --output "$f.asc" "$f"
|
|
||||||
done
|
|
||||||
# clean branch name to get the folder name in S3
|
|
||||||
- name: Get cleaned branch name
|
|
||||||
id: clean_name
|
|
||||||
run: |
|
|
||||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
|
||||||
echo "Cleaned name is ${REF_NAME}"
|
|
||||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
|
||||||
- name: configure aws
|
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
|
||||||
aws-region: ${{ secrets.AWS_REGION }}
|
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
- name: upload binaries to s3
|
|
||||||
run: |
|
|
||||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
|
||||||
- name: create github release
|
|
||||||
run: |
|
|
||||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
||||||
docker-rootful:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: docker/setup-qemu-action@v2
|
|
||||||
- uses: docker/setup-buildx-action@v2
|
|
||||||
- uses: docker/metadata-action@v5
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: gitea/gitea
|
|
||||||
# this will generate tags in the following format:
|
|
||||||
# latest
|
|
||||||
# 1
|
|
||||||
# 1.2
|
|
||||||
# 1.2.3
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: build rootful docker image
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
docker-rootless:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: docker/setup-qemu-action@v2
|
|
||||||
- uses: docker/setup-buildx-action@v2
|
|
||||||
- uses: docker/metadata-action@v5
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: gitea/gitea
|
|
||||||
# each tag below will have the suffix of -rootless
|
|
||||||
flavor: |
|
|
||||||
suffix=-rootless
|
|
||||||
# this will generate tags in the following format (with -rootless suffix added):
|
|
||||||
# latest
|
|
||||||
# 1
|
|
||||||
# 1.2
|
|
||||||
# 1.2.3
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: build rootless docker image
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
file: Dockerfile.rootless
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
@ -24,6 +24,8 @@ rules:
|
|||||||
document-start:
|
document-start:
|
||||||
level: error
|
level: error
|
||||||
present: false
|
present: false
|
||||||
|
ignore: |
|
||||||
|
/.drone.yml
|
||||||
|
|
||||||
document-end:
|
document-end:
|
||||||
present: false
|
present: false
|
||||||
|
@ -37,14 +37,6 @@ func WebAuthn(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, ctx.Session.Get("twofaUid").(int64))
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("HasTwoFactorByUID", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Data["HasTwoFactor"] = hasTwoFactor
|
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplWebAuthn)
|
ctx.HTML(http.StatusOK, tplWebAuthn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/typesniffer"
|
|
||||||
"code.gitea.io/gitea/modules/upload"
|
"code.gitea.io/gitea/modules/upload"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/gitdiff"
|
"code.gitea.io/gitea/services/gitdiff"
|
||||||
@ -61,21 +60,6 @@ func setCompareContext(ctx *context.Context, before, head *git.Commit, headOwner
|
|||||||
return blob
|
return blob
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["GetSniffedTypeForBlob"] = func(blob *git.Blob) typesniffer.SniffedType {
|
|
||||||
st := typesniffer.SniffedType{}
|
|
||||||
|
|
||||||
if blob == nil {
|
|
||||||
return st
|
|
||||||
}
|
|
||||||
|
|
||||||
st, err := blob.GuessContentType()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("GuessContentType failed: %v", err)
|
|
||||||
return st
|
|
||||||
}
|
|
||||||
return st
|
|
||||||
}
|
|
||||||
|
|
||||||
setPathsCompareContext(ctx, before, head, headOwner, headName)
|
setPathsCompareContext(ctx, before, head, headOwner, headName)
|
||||||
setImageCompareContext(ctx)
|
setImageCompareContext(ctx)
|
||||||
setCsvCompareContext(ctx)
|
setCsvCompareContext(ctx)
|
||||||
@ -103,7 +87,16 @@ func setPathsCompareContext(ctx *context.Context, base, head *git.Commit, headOw
|
|||||||
|
|
||||||
// setImageCompareContext sets context data that is required by image compare template
|
// setImageCompareContext sets context data that is required by image compare template
|
||||||
func setImageCompareContext(ctx *context.Context) {
|
func setImageCompareContext(ctx *context.Context) {
|
||||||
ctx.Data["IsSniffedTypeAnImage"] = func(st typesniffer.SniffedType) bool {
|
ctx.Data["IsBlobAnImage"] = func(blob *git.Blob) bool {
|
||||||
|
if blob == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
st, err := blob.GuessContentType()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GuessContentType failed: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
return st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage())
|
return st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,12 +106,7 @@ func ListTasks() TaskTable {
|
|||||||
next = e.NextRun()
|
next = e.NextRun()
|
||||||
prev = e.PreviousRun()
|
prev = e.PreviousRun()
|
||||||
}
|
}
|
||||||
|
|
||||||
task.lock.Lock()
|
task.lock.Lock()
|
||||||
// If the manual run is after the cron run, use that instead.
|
|
||||||
if prev.Before(task.LastRun) {
|
|
||||||
prev = task.LastRun
|
|
||||||
}
|
|
||||||
tTable = append(tTable, &TaskTableRow{
|
tTable = append(tTable, &TaskTableRow{
|
||||||
Name: task.Name,
|
Name: task.Name,
|
||||||
Spec: spec,
|
Spec: spec,
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
system_model "code.gitea.io/gitea/models/system"
|
system_model "code.gitea.io/gitea/models/system"
|
||||||
@ -38,8 +37,6 @@ type Task struct {
|
|||||||
LastMessage string
|
LastMessage string
|
||||||
LastDoer string
|
LastDoer string
|
||||||
ExecTimes int64
|
ExecTimes int64
|
||||||
// This stores the time of the last manual run of this task.
|
|
||||||
LastRun time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoRunAtStart returns if this task should run at the start
|
// DoRunAtStart returns if this task should run at the start
|
||||||
@ -91,12 +88,6 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
graceful.GetManager().RunWithShutdownContext(func(baseCtx context.Context) {
|
graceful.GetManager().RunWithShutdownContext(func(baseCtx context.Context) {
|
||||||
// Store the time of this run, before the function is executed, so it
|
|
||||||
// matches the behavior of what the cron library does.
|
|
||||||
t.lock.Lock()
|
|
||||||
t.LastRun = time.Now()
|
|
||||||
t.lock.Unlock()
|
|
||||||
|
|
||||||
pm := process.GetManager()
|
pm := process.GetManager()
|
||||||
doerName := ""
|
doerName := ""
|
||||||
if doer != nil && doer.ID != -1 {
|
if doer != nil && doer.ID != -1 {
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{if and .PageIsExploreRepositories .OnlyShowRelevant}}
|
{{if and .PageIsExploreRepositories .OnlyShowRelevant}}
|
||||||
<div class="ui message explore-relevancy-note">
|
<div class="ui message explore-relevancy-note">
|
||||||
<span data-tooltip-content="{{ctx.Locale.Tr "explore.relevant_repositories_tooltip"}}">{{ctx.Locale.Tr "explore.relevant_repositories" ((printf "?only_show_relevant=0&sort=%s&q=%s&language=%s" $.SortType (QueryEscape $.Keyword) (QueryEscape $.Language))|Escape) | Safe}}</span>
|
<span data-tooltip-content="{{ctx.Locale.Tr "explore.relevant_repositories_tooltip"}}">{{ctx.Locale.Tr "explore.relevant_repositories" ((print $.Link "?only_show_relevant=0")|Escape) | Safe}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
@ -97,9 +97,7 @@
|
|||||||
{{/*notice: the index of Diff.Files should not be used for element ID, because the index will be restarted from 0 when doing load-more for PRs with a lot of files*/}}
|
{{/*notice: the index of Diff.Files should not be used for element ID, because the index will be restarted from 0 when doing load-more for PRs with a lot of files*/}}
|
||||||
{{$blobBase := call $.GetBlobByPathForCommit $.BeforeCommit $file.OldName}}
|
{{$blobBase := call $.GetBlobByPathForCommit $.BeforeCommit $file.OldName}}
|
||||||
{{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}}
|
{{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}}
|
||||||
{{$sniffedTypeBase := call $.GetSniffedTypeForBlob $blobBase}}
|
{{$isImage := or (call $.IsBlobAnImage $blobBase) (call $.IsBlobAnImage $blobHead)}}
|
||||||
{{$sniffedTypeHead := call $.GetSniffedTypeForBlob $blobHead}}
|
|
||||||
{{$isImage:= or (call $.IsSniffedTypeAnImage $sniffedTypeBase) (call $.IsSniffedTypeAnImage $sniffedTypeHead)}}
|
|
||||||
{{$isCsv := (call $.IsCsvFile $file)}}
|
{{$isCsv := (call $.IsCsvFile $file)}}
|
||||||
{{$showFileViewToggle := or $isImage (and (not $file.IsIncomplete) $isCsv)}}
|
{{$showFileViewToggle := or $isImage (and (not $file.IsIncomplete) $isCsv)}}
|
||||||
{{$isExpandable := or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}}
|
{{$isExpandable := or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}}
|
||||||
@ -200,9 +198,9 @@
|
|||||||
<div id="diff-rendered-{{$file.NameHash}}" class="file-body file-code {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}} gt-overflow-x-scroll">
|
<div id="diff-rendered-{{$file.NameHash}}" class="file-body file-code {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}} gt-overflow-x-scroll">
|
||||||
<table class="chroma gt-w-100">
|
<table class="chroma gt-w-100">
|
||||||
{{if $isImage}}
|
{{if $isImage}}
|
||||||
{{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead "sniffedTypeBase" $sniffedTypeBase "sniffedTypeHead" $sniffedTypeHead}}
|
{{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{template "repo/diff/csv_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead "sniffedTypeBase" $sniffedTypeBase "sniffedTypeHead" $sniffedTypeHead}}
|
{{template "repo/diff/csv_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
{{if or .blobBase .blobHead}}
|
{{if or .blobBase .blobHead}}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<div class="image-diff"
|
<div class="image-diff" data-path-before="{{.root.BeforeRawPath}}/{{PathEscapeSegments .file.OldName}}" data-path-after="{{.root.RawPath}}/{{PathEscapeSegments .file.Name}}">
|
||||||
data-path-before="{{.root.BeforeRawPath}}/{{PathEscapeSegments .file.OldName}}"
|
|
||||||
data-path-after="{{.root.RawPath}}/{{PathEscapeSegments .file.Name}}"
|
|
||||||
data-mime-before="{{.sniffedTypeBase.GetMimeType}}"
|
|
||||||
data-mime-after="{{.sniffedTypeHead.GetMimeType}}"
|
|
||||||
>
|
|
||||||
<div class="ui secondary pointing tabular top attached borderless menu new-menu">
|
<div class="ui secondary pointing tabular top attached borderless menu new-menu">
|
||||||
<div class="new-menu-inner">
|
<div class="new-menu-inner">
|
||||||
<a class="item active" data-tab="diff-side-by-side-{{.file.Index}}">{{ctx.Locale.Tr "repo.diff.image.side_by_side"}}</a>
|
<a class="item active" data-tab="diff-side-by-side-{{.file.Index}}">{{ctx.Locale.Tr "repo.diff.image.side_by_side"}}</a>
|
||||||
|
@ -14,11 +14,9 @@
|
|||||||
<div class="is-loading" style="width: 40px; height: 40px"></div>
|
<div class="is-loading" style="width: 40px; height: 40px"></div>
|
||||||
{{ctx.Locale.Tr "webauthn_press_button"}}
|
{{ctx.Locale.Tr "webauthn_press_button"}}
|
||||||
</div>
|
</div>
|
||||||
{{if .HasTwoFactor}}
|
|
||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
<a href="{{AppSubUrl}}/user/two_factor">{{ctx.Locale.Tr "webauthn_use_twofa"}}</a>
|
<a href="{{AppSubUrl}}/user/two_factor">{{ctx.Locale.Tr "webauthn_use_twofa"}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
@ -283,52 +282,3 @@ func TestAPIRenameUser(t *testing.T) {
|
|||||||
})
|
})
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPICron(t *testing.T) {
|
|
||||||
defer tests.PrepareTestEnv(t)()
|
|
||||||
|
|
||||||
// user1 is an admin user
|
|
||||||
session := loginUser(t, "user1")
|
|
||||||
|
|
||||||
t.Run("List", func(t *testing.T) {
|
|
||||||
defer tests.PrintCurrentTest(t)()
|
|
||||||
|
|
||||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadAdmin)
|
|
||||||
urlStr := fmt.Sprintf("/api/v1/admin/cron?token=%s", token)
|
|
||||||
req := NewRequest(t, "GET", urlStr)
|
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
|
||||||
|
|
||||||
assert.Equal(t, "28", resp.Header().Get("X-Total-Count"))
|
|
||||||
|
|
||||||
var crons []api.Cron
|
|
||||||
DecodeJSON(t, resp, &crons)
|
|
||||||
assert.Len(t, crons, 28)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Execute", func(t *testing.T) {
|
|
||||||
defer tests.PrintCurrentTest(t)()
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin)
|
|
||||||
// Archive cleanup is harmless, because in the test environment there are none
|
|
||||||
// and is thus an NOOP operation and therefore doesn't interfere with any other
|
|
||||||
// tests.
|
|
||||||
urlStr := fmt.Sprintf("/api/v1/admin/cron/archive_cleanup?token=%s", token)
|
|
||||||
req := NewRequest(t, "POST", urlStr)
|
|
||||||
MakeRequest(t, req, http.StatusNoContent)
|
|
||||||
|
|
||||||
// Check for the latest run time for this cron, to ensure it has been run.
|
|
||||||
urlStr = fmt.Sprintf("/api/v1/admin/cron?token=%s", token)
|
|
||||||
req = NewRequest(t, "GET", urlStr)
|
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
|
||||||
|
|
||||||
var crons []api.Cron
|
|
||||||
DecodeJSON(t, resp, &crons)
|
|
||||||
|
|
||||||
for _, cron := range crons {
|
|
||||||
if cron.Name == "archive_cleanup" {
|
|
||||||
assert.True(t, now.Before(cron.Prev))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -11,7 +11,7 @@ import {htmlEscape} from 'escape-goat';
|
|||||||
import {showTemporaryTooltip} from '../modules/tippy.js';
|
import {showTemporaryTooltip} from '../modules/tippy.js';
|
||||||
import {confirmModal} from './comp/ConfirmModal.js';
|
import {confirmModal} from './comp/ConfirmModal.js';
|
||||||
import {showErrorToast} from '../modules/toast.js';
|
import {showErrorToast} from '../modules/toast.js';
|
||||||
import {request, POST} from '../modules/fetch.js';
|
import {request} from '../modules/fetch.js';
|
||||||
|
|
||||||
const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
|
const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
|
||||||
|
|
||||||
@ -243,8 +243,9 @@ export function initGlobalDropzone() {
|
|||||||
this.on('removedfile', (file) => {
|
this.on('removedfile', (file) => {
|
||||||
$(`#${file.uuid}`).remove();
|
$(`#${file.uuid}`).remove();
|
||||||
if ($dropzone.data('remove-url')) {
|
if ($dropzone.data('remove-url')) {
|
||||||
POST($dropzone.data('remove-url'), {
|
$.post($dropzone.data('remove-url'), {
|
||||||
data: new URLSearchParams({file: file.uuid}),
|
file: file.uuid,
|
||||||
|
_csrf: csrfToken,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import {GET} from '../modules/fetch.js';
|
import {hideElem} from '../utils/dom.js';
|
||||||
import {hideElem, loadElem} from '../utils/dom.js';
|
|
||||||
import {parseDom} from '../utils.js';
|
|
||||||
|
|
||||||
function getDefaultSvgBoundsIfUndefined(text, src) {
|
function getDefaultSvgBoundsIfUndefined(svgXml, src) {
|
||||||
const DefaultSize = 300;
|
const DefaultSize = 300;
|
||||||
const MaxSize = 99999;
|
const MaxSize = 99999;
|
||||||
|
|
||||||
const svgDoc = parseDom(text, 'image/svg+xml');
|
const svg = svgXml.documentElement;
|
||||||
const svg = svgDoc.documentElement;
|
|
||||||
const width = svg?.width?.baseVal;
|
const width = svg?.width?.baseVal;
|
||||||
const height = svg?.height?.baseVal;
|
const height = svg?.height?.baseVal;
|
||||||
if (width === undefined || height === undefined) {
|
if (width === undefined || height === undefined) {
|
||||||
@ -68,53 +65,73 @@ export function initImageDiff() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.image-diff:not([data-image-diff-loaded])').each(async function() {
|
$('.image-diff:not([data-image-diff-loaded])').each(function() {
|
||||||
const $container = $(this);
|
const $container = $(this);
|
||||||
$container.attr('data-image-diff-loaded', 'true');
|
$container.attr('data-image-diff-loaded', 'true');
|
||||||
|
|
||||||
// the container may be hidden by "viewed" checkbox, so use the parent's width for reference
|
// the container may be hidden by "viewed" checkbox, so use the parent's width for reference
|
||||||
const diffContainerWidth = Math.max($container.closest('.diff-file-box').width() - 300, 100);
|
const diffContainerWidth = Math.max($container.closest('.diff-file-box').width() - 300, 100);
|
||||||
|
const pathAfter = $container.data('path-after');
|
||||||
|
const pathBefore = $container.data('path-before');
|
||||||
|
|
||||||
const imageInfos = [{
|
const imageInfos = [{
|
||||||
path: this.getAttribute('data-path-after'),
|
loaded: false,
|
||||||
mime: this.getAttribute('data-mime-after'),
|
path: pathAfter,
|
||||||
$images: $container.find('img.image-after'), // matches 3 <img>
|
$image: $container.find('img.image-after'),
|
||||||
$boundsInfo: $container.find('.bounds-info-after')
|
$boundsInfo: $container.find('.bounds-info-after')
|
||||||
}, {
|
}, {
|
||||||
path: this.getAttribute('data-path-before'),
|
loaded: false,
|
||||||
mime: this.getAttribute('data-mime-before'),
|
path: pathBefore,
|
||||||
$images: $container.find('img.image-before'), // matches 3 <img>
|
$image: $container.find('img.image-before'),
|
||||||
$boundsInfo: $container.find('.bounds-info-before')
|
$boundsInfo: $container.find('.bounds-info-before')
|
||||||
}];
|
}];
|
||||||
|
|
||||||
await Promise.all(imageInfos.map(async (info) => {
|
for (const info of imageInfos) {
|
||||||
const [success] = await Promise.all(Array.from(info.$images, (img) => {
|
if (info.$image.length > 0) {
|
||||||
return loadElem(img, info.path);
|
$.ajax({
|
||||||
}));
|
url: info.path,
|
||||||
// only the first images is associated with $boundsInfo
|
success: (data, _, jqXHR) => {
|
||||||
if (!success) info.$boundsInfo.text('(image error)');
|
info.$image.on('load', () => {
|
||||||
if (info.mime === 'image/svg+xml') {
|
info.loaded = true;
|
||||||
const resp = await GET(info.path);
|
setReadyIfLoaded();
|
||||||
const text = await resp.text();
|
}).on('error', () => {
|
||||||
const bounds = getDefaultSvgBoundsIfUndefined(text, info.path);
|
info.loaded = true;
|
||||||
|
setReadyIfLoaded();
|
||||||
|
info.$boundsInfo.text('(image error)');
|
||||||
|
});
|
||||||
|
info.$image.attr('src', info.path);
|
||||||
|
|
||||||
|
if (jqXHR.getResponseHeader('Content-Type') === 'image/svg+xml') {
|
||||||
|
const bounds = getDefaultSvgBoundsIfUndefined(data, info.path);
|
||||||
if (bounds) {
|
if (bounds) {
|
||||||
info.$images.attr('width', bounds.width);
|
info.$image.attr('width', bounds.width);
|
||||||
info.$images.attr('height', bounds.height);
|
info.$image.attr('height', bounds.height);
|
||||||
hideElem(info.$boundsInfo);
|
hideElem(info.$boundsInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
info.loaded = true;
|
||||||
|
setReadyIfLoaded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const $imagesAfter = imageInfos[0].$images;
|
function setReadyIfLoaded() {
|
||||||
const $imagesBefore = imageInfos[1].$images;
|
if (imageInfos[0].loaded && imageInfos[1].loaded) {
|
||||||
|
initViews(imageInfos[0].$image, imageInfos[1].$image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initSideBySide(createContext($imagesAfter[0], $imagesBefore[0]));
|
function initViews($imageAfter, $imageBefore) {
|
||||||
if ($imagesAfter.length > 0 && $imagesBefore.length > 0) {
|
initSideBySide(createContext($imageAfter[0], $imageBefore[0]));
|
||||||
initSwipe(createContext($imagesAfter[1], $imagesBefore[1]));
|
if ($imageAfter.length > 0 && $imageBefore.length > 0) {
|
||||||
initOverlay(createContext($imagesAfter[2], $imagesBefore[2]));
|
initSwipe(createContext($imageAfter[1], $imageBefore[1]));
|
||||||
|
initOverlay(createContext($imageAfter[2], $imageBefore[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
$container.find('> .image-diff-tabs').removeClass('is-loading');
|
$container.find('> .image-diff-tabs').removeClass('is-loading');
|
||||||
|
}
|
||||||
|
|
||||||
function initSideBySide(sizes) {
|
function initSideBySide(sizes) {
|
||||||
let factor = 1;
|
let factor = 1;
|
||||||
|
@ -11,7 +11,9 @@ const safeMethods = new Set(['GET', 'HEAD', 'OPTIONS', 'TRACE']);
|
|||||||
export function request(url, {method = 'GET', headers = {}, data, body, ...other} = {}) {
|
export function request(url, {method = 'GET', headers = {}, data, body, ...other} = {}) {
|
||||||
let contentType;
|
let contentType;
|
||||||
if (!body) {
|
if (!body) {
|
||||||
if (data instanceof FormData || data instanceof URLSearchParams) {
|
if (data instanceof FormData) {
|
||||||
|
body = data;
|
||||||
|
} else if (data instanceof URLSearchParams) {
|
||||||
body = data;
|
body = data;
|
||||||
} else if (isObject(data) || Array.isArray(data)) {
|
} else if (isObject(data) || Array.isArray(data)) {
|
||||||
contentType = 'application/json';
|
contentType = 'application/json';
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {h} from 'vue';
|
import {h} from 'vue';
|
||||||
import {parseDom, serializeXml} from './utils.js';
|
|
||||||
import giteaDoubleChevronLeft from '../../public/assets/img/svg/gitea-double-chevron-left.svg';
|
import giteaDoubleChevronLeft from '../../public/assets/img/svg/gitea-double-chevron-left.svg';
|
||||||
import giteaDoubleChevronRight from '../../public/assets/img/svg/gitea-double-chevron-right.svg';
|
import giteaDoubleChevronRight from '../../public/assets/img/svg/gitea-double-chevron-right.svg';
|
||||||
import giteaEmptyCheckbox from '../../public/assets/img/svg/gitea-empty-checkbox.svg';
|
import giteaEmptyCheckbox from '../../public/assets/img/svg/gitea-empty-checkbox.svg';
|
||||||
@ -146,19 +145,22 @@ const svgs = {
|
|||||||
// At the moment, developers must check, pick and fill the names manually,
|
// At the moment, developers must check, pick and fill the names manually,
|
||||||
// most of the SVG icons in assets couldn't be used directly.
|
// most of the SVG icons in assets couldn't be used directly.
|
||||||
|
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const serializer = new XMLSerializer();
|
||||||
|
|
||||||
// retrieve an HTML string for given SVG icon name, size and additional classes
|
// retrieve an HTML string for given SVG icon name, size and additional classes
|
||||||
export function svg(name, size = 16, className = '') {
|
export function svg(name, size = 16, className = '') {
|
||||||
if (!(name in svgs)) throw new Error(`Unknown SVG icon: ${name}`);
|
if (!(name in svgs)) throw new Error(`Unknown SVG icon: ${name}`);
|
||||||
if (size === 16 && !className) return svgs[name];
|
if (size === 16 && !className) return svgs[name];
|
||||||
|
|
||||||
const document = parseDom(svgs[name], 'image/svg+xml');
|
const document = parser.parseFromString(svgs[name], 'image/svg+xml');
|
||||||
const svgNode = document.firstChild;
|
const svgNode = document.firstChild;
|
||||||
if (size !== 16) {
|
if (size !== 16) {
|
||||||
svgNode.setAttribute('width', String(size));
|
svgNode.setAttribute('width', String(size));
|
||||||
svgNode.setAttribute('height', String(size));
|
svgNode.setAttribute('height', String(size));
|
||||||
}
|
}
|
||||||
if (className) svgNode.classList.add(...className.split(/\s+/).filter(Boolean));
|
if (className) svgNode.classList.add(...className.split(/\s+/).filter(Boolean));
|
||||||
return serializeXml(svgNode);
|
return serializer.serializeToString(svgNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function svgParseOuterInner(name) {
|
export function svgParseOuterInner(name) {
|
||||||
@ -174,7 +176,7 @@ export function svgParseOuterInner(name) {
|
|||||||
if (p1 === -1 || p2 === -1) throw new Error(`Invalid SVG icon: ${name}`);
|
if (p1 === -1 || p2 === -1) throw new Error(`Invalid SVG icon: ${name}`);
|
||||||
const svgInnerHtml = svgStr.slice(p1 + 1, p2);
|
const svgInnerHtml = svgStr.slice(p1 + 1, p2);
|
||||||
const svgOuterHtml = svgStr.slice(0, p1 + 1) + svgStr.slice(p2);
|
const svgOuterHtml = svgStr.slice(0, p1 + 1) + svgStr.slice(p2);
|
||||||
const svgDoc = parseDom(svgOuterHtml, 'image/svg+xml');
|
const svgDoc = parser.parseFromString(svgOuterHtml, 'image/svg+xml');
|
||||||
const svgOuter = svgDoc.firstChild;
|
const svgOuter = svgDoc.firstChild;
|
||||||
return {svgOuter, svgInnerHtml};
|
return {svgOuter, svgInnerHtml};
|
||||||
}
|
}
|
||||||
|
@ -128,14 +128,3 @@ export function decodeURLEncodedBase64(base64url) {
|
|||||||
.replace(/_/g, '/')
|
.replace(/_/g, '/')
|
||||||
.replace(/-/g, '+'));
|
.replace(/-/g, '+'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const domParser = new DOMParser();
|
|
||||||
const xmlSerializer = new XMLSerializer();
|
|
||||||
|
|
||||||
export function parseDom(text, contentType) {
|
|
||||||
return domParser.parseFromString(text, contentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serializeXml(node) {
|
|
||||||
return xmlSerializer.serializeToString(node);
|
|
||||||
}
|
|
||||||
|
@ -183,14 +183,3 @@ export function autosize(textarea, {viewportMarginBottom = 0} = {}) {
|
|||||||
export function onInputDebounce(fn) {
|
export function onInputDebounce(fn) {
|
||||||
return debounce(300, fn);
|
return debounce(300, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the `src` attribute on an element and returns a promise that resolves once the element
|
|
||||||
// has loaded or errored. Suitable for all elements mention in:
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/load_event
|
|
||||||
export function loadElem(el, src) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
el.addEventListener('load', () => resolve(true), {once: true});
|
|
||||||
el.addEventListener('error', () => resolve(false), {once: true});
|
|
||||||
el.src = src;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user