Compare commits

..

1 Commits

Author SHA1 Message Date
bircni
4654e7eccd docs: Changelog for 1.27.0-rc0 (#38247)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-06-29 21:06:07 +02:00
87 changed files with 1682 additions and 1967 deletions

View File

@@ -16,34 +16,34 @@ runs:
using: composite
steps:
- if: ${{ github.workflow == 'cache-seeder' }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/go/pkg/mod
key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
- if: ${{ github.workflow != 'cache-seeder' }}
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/go/pkg/mod
key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
restore-keys: gomod-${{ runner.os }}-${{ runner.arch }}
- if: ${{ github.workflow == 'cache-seeder' && inputs.lint-cache != 'true' }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/go-build
key: gobuild-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
- if: ${{ github.workflow != 'cache-seeder' || inputs.lint-cache == 'true' }}
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/go-build
key: gobuild-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
restore-keys: gobuild-${{ runner.os }}-${{ runner.arch }}
- if: ${{ inputs.lint-cache == 'true' && github.workflow == 'cache-seeder' }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/golangci-lint
key: golint-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum', '.golangci.yml') }}
- if: ${{ inputs.lint-cache == 'true' && github.workflow != 'cache-seeder' }}
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/golangci-lint
key: golint-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum', '.golangci.yml') }}

View File

@@ -13,10 +13,10 @@ runs:
- if: ${{ inputs.cache == 'true' }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 26
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- if: ${{ inputs.cache != 'true' }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 26
node-version: 24

View File

@@ -35,7 +35,7 @@ jobs:
ref: ${{ github.event.pull_request.base.sha }}
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 26
node-version: 24
# Labels are only synced after the title lints, so an invalid title never reaches the label diff.
- run: node ./tools/ci-tools.ts lint-pr-title
env:

View File

@@ -6,30 +6,40 @@ on:
- main
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build-and-publish:
strategy:
fail-fast: false
matrix:
runner: [ubuntu-24.04, ubuntu-24.04-arm]
runs-on: ${{ matrix.runner }}
runs-on: ubuntu-latest
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
fetch-depth: 0
- uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1.3.0
id: build
- uses: snapcore/action-publish@214b86e5ca036ead1668c79afb81e550e6c54d40 # v1.2.0
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
with:
snap: ${{ steps.build.outputs.snap }}
release: latest/edge
- name: Install snapcraft
run: sudo snap install snapcraft --classic
- name: Authenticate snapcraft
shell: bash
run: snapcraft login --with <(printf '%s' "$SNAPCRAFT_STORE_CREDENTIALS")
- name: Remote build
run: |
snapcraft remote-build \
--launchpad-accept-public-upload \
--build-for=amd64,arm64,armhf
- name: List built snaps
run: find . -maxdepth 1 -type f -name '*.snap' -print
- name: Upload and release snapcraft nightly build
run: |
set -euo pipefail
for snap in ./*.snap; do
echo "Uploading $snap to edge"
snapcraft upload --release="latest/edge" "$snap"
done

View File

@@ -23,7 +23,12 @@ jobs:
with:
go-version-file: go.mod
check-latest: true
- uses: ./.github/actions/node-setup
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- run: make deps-frontend deps-backend
# xgo build
- run: make release

View File

@@ -24,7 +24,12 @@ jobs:
with:
go-version-file: go.mod
check-latest: true
- uses: ./.github/actions/node-setup
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- run: make deps-frontend deps-backend
# xgo build
- run: make release

View File

@@ -27,7 +27,12 @@ jobs:
with:
go-version-file: go.mod
check-latest: true
- uses: ./.github/actions/node-setup
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- run: make deps-frontend deps-backend
# xgo build
- run: make release

View File

@@ -4,6 +4,274 @@ This changelog goes through the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com).
## [1.27.0-rc0](https://github.com/go-gitea/gitea/releases/tag/v1.27.0-rc0) - 2026-06-28
* BREAKING
* Feat(actions)!: improve support for reusable workflows (#37478)
* Use Content-Security-Policy: script nonce (#37232)
* SECURITY
* Fix(deps): update module github.com/go-git/go-git/v5 to v5.19.1 [security] (#37786)
* Fix(oauth): restrict introspection to the token's client (#38042)
* Fix(api): don't expose private org membership via public_members (#38145)
* Fix(actions): deny fork-PR cross-repo access via collaborative owner (#38214)
* Fix(migrations): prevent path traversal in repository restore (#38215)
* FEATURES
* Feat(actions): add workflow status badge modal (#38196)
* Feat(actions): support owner-level and global scoped workflows (#38154)
* Feat(api): support ref suffixes in compare (#38148)
* Feat(actions): implement `jobs.<job_id>.continue-on-error` (#38100)
* Feat(actions): show run status on browser tab favicon (#38071)
* Feat(api): add token introspection and self-deletion endpoint (#37995)
* Feat(api): add q parameter to list branches API for server-side filtering (#37982)
* Feat(repo): split repository creation limit into user and org scopes (#37872)
* Feat(actions): bulk delete, disable and enable runners in admin UI (#37869)
* Feat(actions): List workflows that were executed once but got removed from the default branch (#37835)
* Feat(org): add team visibility so org members can discover teams (#37680)
* Feat: add raw diff/patch endpoint for repository comparisons (#37632)
* Feat: Add avatar stacks (#37594)
* Feat(actions): add job summaries (GITHUB_STEP_SUMMARY) (#37500)
* Feat(web): Add Jupyter Notebook (.ipynb) Rendering Support (#37433)
* Support for Custom URI Schemes in OAuth2 Redirect URIs (#37356)
* Feat(orgs): Add search bar for organization members tab page (#37347)
* Feat(api): Add assignees APIs (#37330)
* Feat(api): Add GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs (#37196)
* Serve OpenAPI 3.0 spec at /openapi.v1.json (#37038)
* Add project column picker to issue and pull request sidebar (#37037)
* Allow multiple projects per issue and pull requests (#36784)
* Feat(ui): add "follow rename" to file commit history list (#34994)
* Feat(ssh): auto generate additional ssh keys (#33974)
* ENHANCEMENTS
* Enhance: allow builtin default git config options to be overridden (#38172)
* Enhance: allow MathML core elements (#38034)
* Enhance(markup): improve issue title rendering (#37908)
* Enhance(actions): set descriptive browser tab title on run view (#37870)
* Enhance: Migrate remaining gopkg.in/yaml.v3 usages to go.yaml.in/yaml/v4 (#37866)
* Enhance(actions): show workflow name from YAML instead of filename (#37833)
* Feat(actions): add before/after to PR synchronize event payload (#37827)
* Enhance(actions): add branch filters to run list (#37826)
* Enhance(actions): Make Summary UI more beautiful with more infos (#37824)
* Feat: add copy button to action step header, improve other copy buttons (#37744)
* Fix(icon): use repo-forked icon to display forks count (#37731)
* Feat(api): add sort and order query parameters to job list endpoints (#37672)
* Feat(api): add last_sync to repository API (#37566)
* Enhance: Adjust Workflow Graph styling (#37497)
* Improve code editor text selection and clean up lint enablement (#37474)
* Add mirror auth updates to repo edit API and settings (#37468)
* Replace `olivere/elastic` with REST API client, add OpenSearch support (#37411)
* Feat: Add default PR branch update style setting (#37410)
* Fix inconsistent disabled styling on logged-out repo header buttons (#37406)
* Allow fast-forward-only merge when signed commits are required (#37335)
* Enhance styling in actions page (#37323)
* Fix: improve actions status icons and texts (#37206)
* Make Markdown fenced code block work with more syntaxes (#37154)
* Fix: Sort action run jobs by JobID and Name with matrix examples (#37046)
* Add API endpoint to reply to pull request review comments (#36683)
* PERFORMANCE
* Perf(web): sort the action_run query by a repo-scoped index when possible (#38155)
* Perf: Various performance regression fixes (#38078)
* Perf: extend action `c_u` index to include `created_unix` for faster dashboard feeds (#38076)
* Batch-load related data in actions run, job, and task API endpoints (#37032)
* BUGFIXES
* Fix: update npm dependencies, fix misc issues (#38257)
* Fix(api): respect since/until when counting commits for X-Total-Count (#38204)
* Fix: codemirror regressions (#38248)
* Fix(api): support HEAD requests on all API GET endpoints (#38245)
* Fix(actions): Cleanup workflow status badge code (#38241)
* Fix(web): Correctly align the "disabled" label on larger workflow names (#38240)
* Fix(actions): don't swallow HTML entities into linkified URLs (#38239)
* Fix(packages): accept npm "repository" and "bin" in string form (#38236)
* Fix(actions): fix 500 error when canceling a canceling task (#38223)
* Fix(deps): update module golang.org/x/image to v0.43.0 [security] (#38219)
* Fix(mssql): convert legacy DATETIME columns to DATETIME2 (#38216)
* Fix(api): deny private org member enumeration via /members (#38213)
* Fix(actions): ensure all waiting jobs get runners in large workflows (#38200)
* Fix(deps): update go dependencies (#38194)
* Fix(deps): update npm dependencies (#38193)
* Fix(cli): default must-change-password to false for bot users (#38175)
* Fix(actions): show run index in run view and fix summary graph height (#38165)
* Fix: csp (#38162)
* Fix(deps): update npm dependencies (#38123)
* Fix(mssql): expand legacy issue and comment long-text columns (#38120)
* Fix(packages): validate debian distribution and component names (#38116)
* Fix(packages): validate module version in goproxy ParsePackage (#38104)
* Fix(deps): update dependency esbuild to v0.28.1 [security] (#38097)
* Fix: git push hook post receive (#38089)
* Fix(ui): prevent commit status popup overflowing its row (#38081)
* Fix: validate gem name in rubygems parseMetadataFile (#38061)
* Fix: commit display name (#38057)
* Fix: csp regressions (#38047)
* Fix: api error message (#38031)
* Fix(deps): update npm dependencies (#38029)
* Fix: pgsql lint (#38022)
* Fix(indexer): fix assignee filters in issue search (#38021)
* Fix: various dropdown problems (#38020)
* Fix: refactor git error handling and make archive streaming handle non-existing commit id (#38007)
* Fix: raise git required version to 2.13 (#37996)
* Fix: remove "no-transfrom" from the cache-control header (#37985)
* Fix(deps): update module github.com/google/go-github/v87 to v88 (#37971)
* Fix: use committer time where ever possible as default (#37969)
* Fix(deps): update npm dependencies, remove nolyfill (#37968)
* Fix(deps): update go dependencies (#37967)
* Fix(pull): preserve squash message trailers and additional commit messages (#37954)
* Fix(deps): update module golang.org/x/image to v0.41.0 [security] (#37904)
* Fix: support ##[command] log prefix in action run UI (#37882)
* Fix(deps): update module github.com/google/go-github/v86 to v87 (#37845)
* Fix(deps): update npm dependencies (#37844)
* Fix(deps): update go dependencies (#37841)
* Fix(frontend): resolve Vite assets by manifest source path (#37836)
* Fix(locales): Replace hardcoded strings (#37788)
* Fix(packages): render markdown links relative to linked repo (#37676)
* Fix: persist mirror repository metadata (#37519)
* Fix cmd tests by mocking builtin paths (#37369)
* Add `form-fetch-action` to some forms, fix "fetch action" resp bug (#37305)
* Feat: execute post run cleanup when workflow is cancelled (#37275)
* Fix `relative-time` error and improve global error handler (#37241)
* Refactor flash message and remove SanitizeHTML template func (#37179)
* TESTING
* Test: speed up two tests (#37905)
* Test: Fix random failure test (#37887)
* Test: fix flaky `issue-comment` close test (#37880)
* Test: enable WAL for sqlite integration tests (#37861)
* Test: fix flaky `TestResourceIndex` and reduce its runtime (#37847)
* Test: run `TestAPIRepoMigrate` offline via a local clone source (#37817)
* Ci: shard tests and reduce redundant work (#37618)
* Test(e2e): run playwright via container (#37300)
* Remove external service dependencies in migration tests (#36866)
* BUILD
* Fix(actions): authenticate snapcraft before nightly remote build (#38252)
* Ci: cap Elasticsearch heap in db-tests (#37816)
* Build(snap): publish nightly version to snapcraft via actions (#37814)
* Ci: split pgsql shards into plain jobs, dedupe setup actions (#37802)
* Ci: narrow files-changed frontend filter (#37749)
* Ci: add `zizmor` to `lint-actions` (#37720)
* Chore: clean up "contrib" dir (#37690)
* Fix: snap build (main branch) (#37685)
* Ci: Also lint json5 files (#37659)
* Feat(editor): broaden language detection in web code editor (#37619)
* Build: update pnpm to v11 (#37591)
* Refactor(deps): migrate from `nektos/act` fork to `gitea/runner` (#37557)
* Refactor: lint bare `fill`/`stroke` colors, add vars for git graph color series (#37543)
* Update go js py dependencies (#37525)
* Ci: lint PR titles with commitlint (#37498)
* Chore: upgrade Go version in devcontainer image to 1.26 (#37374)
* Update GitHub Actions to latest major versions (#37313)
* Update go js dependencies (#37312)
* Fail vite build on rolldown warnings via NODE_ENV=test (#37270)
* Remove htmx (#37224)
* Replace custom Go formatter with `golangci-lint fmt` (#37194)
* Refactor htmx and fetch-action related code (#37186)
* Integrate renovate bot for all dependency updates (#37050)
* Build(sign): move to sigstore (#38250)
* DOCS
* Docs: update changelog for 1.26.3 & 1.26.4 (#38178)
* Docs: fix duplicated word in foreachref doc comment (#38161)
* Docs: Clarify criteria for becoming a merger (#38113)
* Docs: Publish TOC Election Result 2026 (#38111)
* Docs: mark openapi3 as autogenerated in attributes (#37963)
* Docs: add development setup guide (#37960)
* MISC
* Revert(sign): restore gpg (#38251)
* Refactor: replace legacy `delete-button` with `link-action` (#38143)
* Refactor(actions): read runner capabilities from proto field (#38068)
* Refactor(api): clarify APIError message usage and fix legacy lint error (#38012)
* Refactor: Use db.Get[] instead of db.GetEngine(ctx).Get(bean) to avoid zero value fetching wrong database record (#37977)
* Fix(deps): update go dependencies (#37851)
* Ci: Fix sync PR labels from the conventional-commit title (#37784) (#37825)
* Ci: tweak `files-changed`, add `free-disk-space` (#37819)
* Fix(deps): update module golang.org/x/crypto to v0.52.0 [security] (#37806)
* Test(e2e): add comment, release, star, PR and fork tests (#37800)
* Chore: simplify issue and pull request templates (#37799)
* Chore: Update giteabot to fix failure when backport (#37789)
* Fix(api): handle partial failures in push mirror synchronization gracefully (#37782)
* Fix(deps): update module gitlab.com/gitlab-org/api/client-go/v2 to v2.26.0 (#37771)
* Ci: split giteabot workflow (#37770)
* Fix(deps): update npm dependencies (#37768)
* Refactor(waitgroup): replace Add/Done goroutines with WaitGroup.Go (#37764)
* Fix(deps): update module google.golang.org/grpc to v1.81.1 (#37762)
* Ci: fix cache-related issues (#37761)
* Chore: fix tests (#37760)
* Fix(deps): update module github.com/google/go-github/v85 to v86 (#37754)
* Fix(deps): update npm dependencies (#37753)
* Fix(deps): update go dependencies (#37752)
* Chore(deps): update action dependencies (#37751)
* Fix(markup): wrap indented code blocks for the code-copy button (#37748)
* Chore(db): introduce db.Session and db.EngineMigration interfaces (#37746)
* Feat(web): also display PR counts in repo list (#37739)
* Refactor(glob): use strings.Builder for regexp compilation (#37730)
* Chore(doctor): remove four obsolete doctor check implementations (#37728)
* Refactor(org): simplify owner-team org repo creation logic (#37727)
* Refactor: move `workflowpattern` into `modules/actions` (#37717)
* Chore: clean up tests (#37715)
* Style: misc UI fixes (#37691)
* Ci: add shellcheck linter (#37682)
* Fix: catch and fix more lint problems (#37674)
* Fix(deps): update dependency mermaid to v11.15.0 [security], add e2e test (#37662)
* Fix(deps): update npm dependencies (#37647)
* Ci(renovate): update Go import paths on major bumps (#37641)
* Fix(deps): update go dependencies (major) (#37639)
* Chore(deps): update action dependencies (major) (#37638)
* Fix(deps): update module code.gitea.io/sdk/gitea to v0.25.0 (#37637)
* Fix(deps): update npm dependencies (#37636)
* Refactor(log): replace log.Critical with log.Error (#37624)
* Build(deps): bump fast-uri from 3.1.0 to 3.1.2 (#37616)
* Feat(oauth): Support AWS Cognito OAuth2 provider (#37607)
* Chore(deps): update action dependencies (#37603)
* Ci: allow `chore` type in PR title lint (#37575)
* Refactor: only reset a database table when the table's data was changed (#37573)
* Ci: increase renovate frequency and fix RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS (#37565)
* Refactor: use modernc sqlite driver as default (#37562)
* Docs: fix 4 typos in CHANGELOG.md (#37549)
* Fix(deps): update go dependencies (#37541)
* Chore(deps): update action dependencies (#37540)
* Refactor pull request view (6) (#37522)
* Fix: redirect early CLI console logger to stderr (#37507)
* Refactor "flex-list" to "flex-divided-list" (#37505)
* Refactor compare diff/pull page (1) (#37481)
* Refactor pull request view (4) (#37451)
* Update 1.26.1 changelog in main (#37442)
* Refactor: use named `Permission` field in `Repository` struct instead of anonymous embedding (#37441)
* Refactor: serve site manifest via `/assets/site-manifest.json` endpoint (#37405)
* Remove IsValidExternalURL/IsAPIURL and use IsValidURL at call sites (#37364)
* Update `Block a user` form (#37359)
* Move review request functions to a standalone file (#37358)
* Feat(security): set X-Content-Type-Options: nosniff by default (#37354)
* Enable strict TypeScript, add `errorMessage` helper (#37292)
* Refactor frontend `tw-justify-between` layouts to `flex-left-right` (#37291)
* Update Nix flake (#37284)
* Fix Repository transferring page (#37277)
* Remove `SubmitEvent` polyfill (#37276)
* Remove dead code identified by `deadcode` tool (#37271)
* Upgrade go-git to v5.18.0 (#37268)
* Don't add useless labels which will bother changelog generation (#37267)
* Move heatmap to first-party code (#37262)
* Tests/integration: simplify code (#37249)
* Add pagination and search box to org teams list (#37245)
* Remove error returns from crypto random helpers and callers (#37240)
* Add `ExternalIDClaim` option for OAuth2 OIDC auth source (#37229)
* Refactor: simplify ParseCatFileTreeLine and catBatchParseTreeEntries (#37210)
* Refactor "htmx" to "fetch action" (#37208)
* Update go js py dependencies (#37204)
* Add comment for the design of "user activity time" (#37195)
* Remove outdated RunUser logic (#37180)
* Models/fixtures: add "DO NOT add more test data" comment to all yml fixture files (#37150)
* Update javascript dependencies (#37142)
* Update go dependencies (#37141)
* Frontport changelog of v1.26.0-rc0 (#37138)
* Introduce `ActionRunAttempt` to represent each execution of a run (#37119)
* Workflow Artifact Info Hover (#37100)
* Extend issue context popup beyond markdown content (#36908)
* Add bulk repository deletion for organizations (#36763)
* Feat: Add bypass allowlist for branch protection (#36514)
## [1.26.4](https://github.com/go-gitea/gitea/releases/tag/1.26.4) - 2026-06-21
* SECURITY

View File

@@ -16,7 +16,7 @@ EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-che
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 # renovate: datasource=go
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15 # renovate: datasource=go
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.8.0 # renovate: datasource=go
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.35.0 # renovate: datasource=go
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.34.1 # renovate: datasource=go
XGO_PACKAGE ?= src.techknowlogick.com/xgo@v1.9.0 # renovate: datasource=go
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.4.0 # renovate: datasource=go
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 # renovate: datasource=go
@@ -231,9 +231,7 @@ endif
generate-swagger: $(SWAGGER_SPEC) $(OPENAPI3_SPEC) ## generate the swagger spec from code comments
$(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT)
@output="$$($(GO) run $(SWAGGER_PACKAGE) generate spec --enable-allof-compounding --skip-enum-desc --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)' 2>&1)" || { printf '%s\n' "$$output" >&2; exit 1; }; \
warnings="$$(printf '%s\n' "$$output" | grep -v '^go: ')"; \
if [ -n "$$warnings" ]; then printf '%s\n' "$$warnings" >&2; exit 1; fi
$(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)'
.PHONY: swagger-check
swagger-check: generate-swagger
@@ -248,11 +246,9 @@ swagger-check: generate-swagger
swagger-validate: ## check if the swagger spec is valid
@# swagger "validate" requires that the "basePath" must start with a slash, but we are using Golang template "{{...}}"
@$(SED_INPLACE) -E -e 's|"basePath":( *)"(.*)"|"basePath":\1"/\2"|g' './$(SWAGGER_SPEC)' # add a prefix slash to basePath
@output="$$($(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)' 2>&1)"; status=$$?; \
$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)'; \
printf '%s\n' "$$output" | grep -v '^go: '; \
[ $$status -eq 0 ] || exit $$status; \
case "$$output" in *WARNING:*) exit 1;; esac
@# FIXME: there are some warnings
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
@$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath
.PHONY: generate-openapi3
generate-openapi3: $(OPENAPI3_SPEC) ## generate the OpenAPI 3.0 spec from the Swagger 2.0 spec

View File

@@ -436,7 +436,6 @@
"jsonl",
"mcmeta",
"sarif",
"slnlaunch",
"tact",
"tfstate",
"topojson",
@@ -692,17 +691,10 @@
"extensions": [
"ini",
"cnf",
"container",
"dof",
"lektorproject",
"mount",
"network",
"prefs",
"properties",
"service",
"socket",
"target",
"timer",
"url",
"conf"
],

View File

@@ -18,7 +18,6 @@ func newUserCommand() *cli.Command {
microcmdUserDelete(),
newUserGenerateAccessTokenCommand(),
microcmdUserMustChangePassword(),
microcmdUserDisableTwoFactor(),
},
}
}

View File

@@ -1,72 +0,0 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"errors"
"fmt"
"strings"
auth_model "gitea.dev/models/auth"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"github.com/urfave/cli/v3"
)
func microcmdUserDisableTwoFactor() *cli.Command {
return &cli.Command{
Name: "disable-2fa",
Usage: "Disable two-factor authentication for a user",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "Username of the user to disable 2FA for",
},
&cli.Int64Flag{
Name: "id",
Usage: "ID of the user to disable 2FA for",
},
},
Action: runDisableTwoFactor,
}
}
func runDisableTwoFactor(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") && !c.IsSet("username") {
return errors.New("either --id or --username must be provided")
}
if !setting.IsInTesting {
if err := initDB(ctx); err != nil {
return err
}
}
var user *user_model.User
var err error
if c.IsSet("id") {
user, err = user_model.GetUserByID(ctx, c.Int64("id"))
} else {
user, err = user_model.GetUserByName(ctx, c.String("username"))
}
if err != nil {
return err
}
// When both selectors are given, make sure they refer to the same user.
if c.IsSet("id") && c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
return fmt.Errorf("the user with id %d is %q, which does not match the provided username %q", user.ID, user.Name, c.String("username"))
}
totp, webAuthn, err := auth_model.DisableTwoFactor(ctx, user.ID)
if err != nil {
return err
}
fmt.Printf("Disabled 2FA for user %q (removed %d TOTP and %d WebAuthn credential(s))\n", user.Name, totp, webAuthn)
return nil
}

View File

@@ -1,119 +0,0 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"io"
"strconv"
"testing"
auth_model "gitea.dev/models/auth"
"gitea.dev/models/db"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDisableTwoFactorCommand(t *testing.T) {
ctx := t.Context()
defer func() {
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}, &auth_model.TwoFactor{}, &auth_model.WebAuthnCredential{}))
}()
t.Run("disable TOTP and WebAuthn", func(t *testing.T) {
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "tfuser", "--email", "tfuser@gitea.local", "--random-password"}))
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "tfuser"})
// Enroll TOTP.
tf := &auth_model.TwoFactor{UID: user.ID}
require.NoError(t, tf.SetSecret("test-secret"))
_, err := tf.GenerateScratchToken()
require.NoError(t, err)
require.NoError(t, auth_model.NewTwoFactor(ctx, tf))
// Register a WebAuthn credential.
_, err = auth_model.CreateCredential(ctx, user.ID, "test-key", &webauthn.Credential{ID: []byte("test-cred-id")})
require.NoError(t, err)
has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID)
require.NoError(t, err)
require.True(t, has)
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--username", "tfuser"}))
// Both factors must be gone afterwards.
has, err = auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID)
require.NoError(t, err)
assert.False(t, has)
})
t.Run("disable by id", func(t *testing.T) {
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "iduser", "--email", "iduser@gitea.local", "--random-password"}))
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "iduser"})
tf := &auth_model.TwoFactor{UID: user.ID}
require.NoError(t, tf.SetSecret("test-secret"))
require.NoError(t, auth_model.NewTwoFactor(ctx, tf))
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--id", strconv.FormatInt(user.ID, 10)}))
has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID)
require.NoError(t, err)
assert.False(t, has)
})
t.Run("no enrollment is a no-op", func(t *testing.T) {
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "plainuser", "--email", "plainuser@gitea.local", "--random-password"}))
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--username", "plainuser"}))
})
t.Run("id and username must match when both given", func(t *testing.T) {
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "matchuser", "--email", "matchuser@gitea.local", "--random-password"}))
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "matchuser"})
id := strconv.FormatInt(user.ID, 10)
// Matching id + username is accepted.
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--id", id, "--username", "matchuser"}))
// Mismatched id + username is rejected.
cmd := microcmdUserDisableTwoFactor()
cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard
err := cmd.Run(ctx, []string{"disable-2fa", "--id", id, "--username", "someotheruser"})
require.Error(t, err)
require.Contains(t, err.Error(), "does not match the provided username")
})
t.Run("failure cases", func(t *testing.T) {
testCases := []struct {
name string
args []string
expectedErr string
}{
{
name: "user does not exist",
args: []string{"disable-2fa", "--username", "nonexistentuser"},
expectedErr: "user does not exist",
},
{
name: "neither id nor username",
args: []string{"disable-2fa"},
expectedErr: "either --id or --username must be provided",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cmd := microcmdUserDisableTwoFactor()
cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard
err := cmd.Run(ctx, tc.args)
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
})
}
})
}

View File

@@ -95,15 +95,8 @@ However, if there are no objections from maintainers, the PR can be merged with
### Commit messages
Mergers are required to rewrite the PR title and the first comment (the summary) when necessary so the squash commit message is clear.
Usually the Pull Request description and commit message body should not be empty, unless the title is already clear enough or the description would be a copy of the comments in code.
The final commit message:
- should match the code changes.
- should only keep true co-authors, false-positive co-authors should be removed.
- should not hedge: replace phrases like `hopefully, <x> won't happen anymore` with definite wording.
- should not contain hidden information like `<!-- -->` or extra information after the description's divider `----`.
- should not contain unrelated contents (e.g.: Release Notes, Configuration, etc.) from a Renovate update PR.
The final commit message should not hedge: replace phrases like `hopefully, <x> won't happen anymore` with definite wording.
#### PR Co-authors
@@ -165,16 +158,9 @@ Any account with write access (including bots and TOC members) **must** use [2FA
Mergers are the maintainers who carry out the final merge of approved PRs. Their responsibilities, described throughout this guide, are:
- Merging PRs from the [merge queue](#getting-prs-merged) in order, once a PR has `lgtm/done`, no open discussions, and no merge conflicts.
- Rewriting the PR title and description prior to the merge, making the [commit message](#commit-messages) clear.\
In particular, mergers should edit the PR description.\
Mergers should **not** edit the actual commit message except to remove unnecessary information. Because of that, even if users are looking at the PR, they can understand what changed.
- Rewriting the PR title and summary so the squash [commit message](#commit-messages) is clear, removing false-positive co-authors while keeping every true co-author.
- Assigning the correct labels (including `type/…`) needed for changelog and backport decisions.
- Agreeing, together with the owners, on when a release is ready (see [release management](release-management.md)).
- Merging a PR also means the PR looks good to the merger and is approved by the merger.
If a merger violates these merge guides more than 3 times in the past 365 days
(e.g.: merge with unresolved reviews without TOC decision to ignore the review, merge with garbage commit messages),
they may lose their merging privileges for at least three months.
#### Becoming a merger
@@ -214,20 +200,20 @@ random.seed("Gitea TOC <YEAR> Election")
random.choice([<CANDIDATE_1>, <CANDIDATE_2>, ...])
```
The result of this script needs then to be published in the TOC election issue to ensure transparency of the process.
The result of this script needs then to be published in the TOC election issue to ensure transparency of the process.
### Current TOC members
- 2026-06-14 ~ 2026-12-31
- Company
- [Yu Tian](https://gitea.com/Zettat123) <zettat123@gmail.com>
- [Jason Song](https://gitea.com/wolfogre) <i@wolfogre.com>
- [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
- [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.com>
- Community
- [bircni](https://gitea.com/bircni) <bircni@icloud.com>
- [delvh](https://gitea.com/delvh) <dev.lh@web.de>
- [TheFox0x7](https://gitea.com/TheFox0x7) <thefox0x7@gmail.com>
### Previous TOC/owners members
@@ -241,7 +227,7 @@ Here's the history of the owners and the time they served:
- [Andrew Thornton](https://gitea.com/zeripath) - [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872), 2023
- [6543](https://gitea.com/6543) - 2023, 2025
- [John Olheiser](https://gitea.com/jolheiser) - 2023, 2024
- [Jason Song](https://gitea.com/wolfogre) - 2023, 2025
- [Jason Song](https://gitea.com/wolfogre) - 2023
## Governance Compensation

View File

@@ -1,4 +1,6 @@
import arrayFunc from 'eslint-plugin-array-func';
import comments from '@eslint-community/eslint-plugin-eslint-comments';
import deMorgan from 'eslint-plugin-de-morgan';
import globals from 'globals';
import importPlugin from 'eslint-plugin-import-x';
import playwright from 'eslint-plugin-playwright';
@@ -13,6 +15,7 @@ import vue from 'eslint-plugin-vue';
import vueScopedCss from 'eslint-plugin-vue-scoped-css';
import wc from 'eslint-plugin-wc';
import {defineConfig, globalIgnores} from 'eslint/config';
import type {ESLint} from 'eslint';
import unescapedHtmlLiteral from './tools/eslint-rules/unescaped-html-literal.ts';
@@ -61,8 +64,10 @@ export default defineConfig([
'@eslint-community/eslint-comments': comments,
'@stylistic': stylistic,
'@typescript-eslint': typescriptPlugin.plugin,
'array-func': arrayFunc,
'de-morgan': deMorgan,
'gitea': {rules: {'unescaped-html-literal': unescapedHtmlLiteral}},
'import-x': importPlugin,
'import-x': importPlugin as unknown as ESLint.Plugin, // https://github.com/un-ts/eslint-plugin-import-x/issues/203
regexp,
sonarjs,
unicorn,
@@ -273,6 +278,12 @@ export default defineConfig([
'@typescript-eslint/unified-signatures': [2],
'accessor-pairs': [2],
'array-callback-return': [2, {checkForEach: true}],
'array-func/avoid-reverse': [2],
'array-func/from-map': [2],
'array-func/no-unnecessary-this-arg': [2],
'array-func/prefer-array-from': [2],
'array-func/prefer-flat-map': [0], // handled by unicorn/prefer-array-flat-map
'array-func/prefer-flat': [0], // handled by unicorn/prefer-array-flat
'arrow-body-style': [0],
'block-scoped-var': [2],
'camelcase': [0],
@@ -283,6 +294,8 @@ export default defineConfig([
'consistent-this': [0],
'constructor-super': [2],
'curly': [0],
'de-morgan/no-negated-conjunction': [2],
'de-morgan/no-negated-disjunction': [2],
'default-case-last': [2],
'default-case': [0],
'default-param-last': [0],
@@ -732,7 +745,6 @@ export default defineConfig([
'unicorn/consistent-json-file-read': [2],
'unicorn/consistent-optional-chaining': [2],
'unicorn/consistent-template-literal-escape': [2],
'unicorn/consistent-tuple-labels': [2],
'unicorn/custom-error-definition': [0],
'unicorn/default-export-style': [2],
'unicorn/dom-node-dataset': [2, {preferAttributes: true}],
@@ -766,7 +778,6 @@ export default defineConfig([
'unicorn/no-array-sort-for-min-max': [2],
'unicorn/no-array-splice': [0],
'unicorn/no-asterisk-prefix-in-documentation-comments': [0],
'unicorn/no-async-promise-finally': [2],
'unicorn/no-await-expression-member': [0],
'unicorn/no-await-in-promise-methods': [2],
'unicorn/no-blob-to-file': [2],
@@ -803,10 +814,8 @@ export default defineConfig([
'unicorn/no-invalid-fetch-options': [2],
'unicorn/no-invalid-file-input-accept': [2],
'unicorn/no-invalid-remove-event-listener': [2],
'unicorn/no-invalid-well-known-symbol-methods': [2],
'unicorn/no-keyword-prefix': [0],
'unicorn/no-late-current-target-access': [2],
'unicorn/no-late-event-control': [2],
'unicorn/no-lonely-if': [2],
'unicorn/no-loop-iterable-mutation': [2],
'unicorn/no-magic-array-flat-depth': [0],
@@ -843,11 +852,9 @@ export default defineConfig([
'unicorn/no-uncalled-method': [2],
'unicorn/no-undeclared-class-members': [2],
'unicorn/no-unnecessary-array-flat-depth': [2],
'unicorn/no-unnecessary-array-flat-map': [2],
'unicorn/no-unnecessary-array-splice-count': [2],
'unicorn/no-unnecessary-await': [2],
'unicorn/no-unnecessary-boolean-comparison': [2],
'unicorn/no-unnecessary-fetch-options': [0],
'unicorn/no-unnecessary-global-this': [0],
'unicorn/no-unnecessary-nested-ternary': [2],
'unicorn/no-unnecessary-polyfills': [2],
@@ -860,7 +867,6 @@ export default defineConfig([
'unicorn/no-unreadable-object-destructuring': [0],
'unicorn/no-unsafe-buffer-conversion': [2],
'unicorn/no-unsafe-dom-html': [0],
'unicorn/no-unsafe-promise-all-settled-values': [2],
'unicorn/no-unsafe-property-key': [0],
'unicorn/no-unsafe-string-replacement': [0],
'unicorn/no-unused-array-method-return': [2],
@@ -890,17 +896,13 @@ export default defineConfig([
'unicorn/number-literal-case': [0],
'unicorn/numeric-separators-style': [0],
'unicorn/operator-assignment': [2],
'unicorn/prefer-abort-signal-any': [2],
'unicorn/prefer-abort-signal-timeout': [2],
'unicorn/prefer-add-event-listener': [2],
'unicorn/prefer-add-event-listener-options': [2],
'unicorn/prefer-aggregate-error': [2],
'unicorn/prefer-array-find': [0], // handled by @typescript-eslint/prefer-find
'unicorn/prefer-array-flat': [2],
'unicorn/prefer-array-flat-map': [2],
'unicorn/prefer-array-from-async': [2],
'unicorn/prefer-array-from-map': [2],
'unicorn/prefer-array-from-range': [2],
'unicorn/prefer-array-index-of': [2],
'unicorn/prefer-array-iterable-methods': [2],
'unicorn/prefer-array-last-methods': [2],
@@ -910,7 +912,6 @@ export default defineConfig([
'unicorn/prefer-await': [2],
'unicorn/prefer-bigint-literals': [2],
'unicorn/prefer-blob-reading-methods': [2],
'unicorn/prefer-block-statement-over-iife': [2],
'unicorn/prefer-boolean-return': [2],
'unicorn/prefer-class-fields': [2],
'unicorn/prefer-classlist-toggle': [2],
@@ -923,18 +924,15 @@ export default defineConfig([
'unicorn/prefer-dom-node-append': [2],
'unicorn/prefer-dom-node-html-methods': [0],
'unicorn/prefer-dom-node-remove': [2],
'unicorn/prefer-dom-node-replace-children': [2],
'unicorn/prefer-dom-node-text-content': [2],
'unicorn/prefer-early-return': [0],
'unicorn/prefer-else-if': [2],
'unicorn/prefer-error-is-error': [0],
'unicorn/prefer-event-target': [2],
'unicorn/prefer-export-from': [0],
'unicorn/prefer-flat-math-min-max': [2],
'unicorn/prefer-get-or-insert-computed': [2],
'unicorn/prefer-global-number-constants': [2],
'unicorn/prefer-global-this': [0],
'unicorn/prefer-group-by': [2],
'unicorn/prefer-has-check': [2],
'unicorn/prefer-hoisting-branch-code': [2],
'unicorn/prefer-https': [0], // false-positives on namespace and schema URIs
@@ -944,7 +942,6 @@ export default defineConfig([
'unicorn/prefer-includes-over-repeated-comparisons': [0], // too opinionated
'unicorn/prefer-iterable-in-constructor': [2],
'unicorn/prefer-iterator-concat': [0], // too opinionated
'unicorn/prefer-iterator-helpers': [2],
'unicorn/prefer-iterator-to-array': [2],
'unicorn/prefer-iterator-to-array-at-end': [2],
'unicorn/prefer-keyboard-event-key': [2],
@@ -969,11 +966,9 @@ export default defineConfig([
'unicorn/prefer-object-destructuring-defaults': [2],
'unicorn/prefer-object-from-entries': [2],
'unicorn/prefer-object-iterable-methods': [2],
'unicorn/prefer-observer-apis': [2],
'unicorn/prefer-optional-catch-binding': [2],
'unicorn/prefer-path2d': [2],
'unicorn/prefer-private-class-fields': [0],
'unicorn/prefer-promise-try': [2],
'unicorn/prefer-promise-with-resolvers': [2],
'unicorn/prefer-prototype-methods': [0],
'unicorn/prefer-query-selector': [2],
@@ -984,12 +979,10 @@ export default defineConfig([
'unicorn/prefer-response-static-json': [2],
'unicorn/prefer-scoped-selector': [0],
'unicorn/prefer-set-has': [0],
'unicorn/prefer-set-methods': [0],
'unicorn/prefer-set-size': [2],
'unicorn/prefer-short-arrow-method': [2],
'unicorn/prefer-simple-condition-first': [0],
'unicorn/prefer-simple-sort-comparator': [2],
'unicorn/prefer-simplified-conditions': [2],
'unicorn/prefer-single-array-predicate': [2],
'unicorn/prefer-single-call': [2],
'unicorn/prefer-single-object-destructuring': [2],
@@ -1009,7 +1002,6 @@ export default defineConfig([
'unicorn/prefer-switch': [0],
'unicorn/prefer-temporal': [0],
'unicorn/prefer-ternary': [0],
'unicorn/prefer-toggle-attribute': [2],
'unicorn/prefer-top-level-await': [0],
'unicorn/prefer-type-error': [0],
'unicorn/prefer-type-literal-last': [0],
@@ -1018,7 +1010,6 @@ export default defineConfig([
'unicorn/prefer-unicode-code-point-escapes': [0],
'unicorn/prefer-url-can-parse': [2],
'unicorn/prefer-url-href': [2],
'unicorn/prefer-url-search-parameters': [2],
'unicorn/prefer-while-loop-condition': [2],
'unicorn/prevent-abbreviations': [0],
'unicorn/relative-url-style': [2],

6
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1782723713,
"narHash": "sha256-oPXCU/SSUokcGaJREHibG1CBX3+s/W7orDWQOZDsEeQ=",
"lastModified": 1776877367,
"narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b5aa0fbd538984f6e3d201be0005b4463d8b09f8",
"rev": "0726a0ecb6d4e08f6adced58726b95db924cef57",
"type": "github"
},
"original": {

View File

@@ -34,7 +34,7 @@
# only bump toolchain versions here
go = pkgs.go_1_26;
nodejs = pkgs.nodejs_26;
nodejs = pkgs.nodejs_24;
python3 = pkgs.python314;
pnpm = pkgs.pnpm_10;

20
go.mod
View File

@@ -13,7 +13,7 @@ require (
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
gitea.dev/actions-proto-go v0.6.0
gitea.dev/sdk v1.2.0
gitea.dev/sdk v1.1.0
github.com/42wim/httpsig v1.2.4
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0
@@ -24,8 +24,8 @@ require (
github.com/PuerkitoBio/goquery v1.12.0
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0
github.com/alecthomas/chroma/v2 v2.27.0
github.com/aws/aws-sdk-go-v2/credentials v1.19.25
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.5
github.com/aws/aws-sdk-go-v2/credentials v1.19.24
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.4
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.6.0
github.com/bohde/codel v0.2.0
@@ -68,8 +68,8 @@ require (
github.com/huandu/xstrings v1.5.0
github.com/jhillyerd/enmime/v2 v2.4.1
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.19.0
github.com/klauspost/cpuid/v2 v2.4.0
github.com/klauspost/compress v1.18.6
github.com/klauspost/cpuid/v2 v2.3.0
github.com/lib/pq v1.12.3
github.com/markbates/goth v1.82.0
github.com/mattn/go-isatty v0.0.22
@@ -78,7 +78,7 @@ require (
github.com/mholt/archives v0.1.5
github.com/microcosm-cc/bluemonday v1.0.27
github.com/microsoft/go-mssqldb v1.10.0
github.com/minio/minio-go/v7 v7.2.1
github.com/minio/minio-go/v7 v7.2.0
github.com/msteinert/pam/v2 v2.1.0
github.com/niklasfasching/go-org v1.9.1
github.com/opencontainers/go-digest v1.0.0
@@ -96,12 +96,12 @@ require (
github.com/tstranex/u2f v1.0.0
github.com/ulikunitz/xz v0.5.15
github.com/urfave/cli-docs/v3 v3.1.0
github.com/urfave/cli/v3 v3.10.1
github.com/urfave/cli/v3 v3.10.0
github.com/wneessen/go-mail v0.7.3
github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.8.2
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
gitlab.com/gitlab-org/api/client-go/v2 v2.44.0
gitlab.com/gitlab-org/api/client-go/v2 v2.42.0
go.yaml.in/yaml/v4 v4.0.0-rc.5
golang.org/x/crypto v0.53.0
golang.org/x/image v0.43.0
@@ -111,14 +111,14 @@ require (
golang.org/x/sync v0.21.0
golang.org/x/sys v0.46.0
golang.org/x/text v0.38.0
google.golang.org/grpc v1.82.0
google.golang.org/grpc v1.81.1
google.golang.org/protobuf v1.36.11
gopkg.in/ini.v1 v1.67.3
modernc.org/sqlite v1.53.0
mvdan.cc/xurls/v2 v2.6.0
strk.kbt.io/projects/go/libravatar v0.0.0-20260301104140-add494e31dab
xorm.io/builder v0.3.13
xorm.io/xorm v1.4.1
xorm.io/xorm v1.3.11
)
require (

42
go.sum
View File

@@ -28,8 +28,8 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGq
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
gitea.dev/actions-proto-go v0.6.0 h1:gjllYQ5vmwlkqOeofTQu5qKTZpmf7kWsafoHvoPCSzY=
gitea.dev/actions-proto-go v0.6.0/go.mod h1:p4RX+D9oqiEEzzkPMXscw2CmaGuYFPWFc6xIOmDNDqs=
gitea.dev/sdk v1.2.0 h1:avRtJl/nKCGispgSalo9czoZM9Rto1awnE0caNAoXGo=
gitea.dev/sdk v1.2.0/go.mod h1:rfh5oNdIK24cbCREwIn1tqWKQW+IICXFGWJyebuOAOE=
gitea.dev/sdk v1.1.0 h1:wLlz03WkLEiXa2bQpO1JQBTlYf7tQI2neYtZK1kU+TE=
gitea.dev/sdk v1.1.0/go.mod h1:Zfl+EZXdsGGCLkryDfsmvYrQo6GKMl4U3BJA8Beu+cs=
github.com/42wim/httpsig v1.2.4 h1:mI5bH0nm4xn7K18fo1K3okNDRq8CCJ0KbBYWyA6r8lU=
github.com/42wim/httpsig v1.2.4/go.mod h1:yKsYfSyTBEohkPik224QPFylmzEBtda/kjyIAJjh3ps=
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432 h1:3Fcz1QzlS7Jv4FT2KI3cHNSZL+KPN3dXxurn9f3YL/Y=
@@ -94,14 +94,14 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA=
github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg=
github.com/aws/aws-sdk-go-v2/credentials v1.19.25 h1:TzPVjfUZ1hsKafvYE+DIzKXIik2KufQxsPHanlkttbo=
github.com/aws/aws-sdk-go-v2/credentials v1.19.25/go.mod h1:K4hw0buguVvtC74HnVfTRr0LzQQHAWPqJbBU9QGk2Pg=
github.com/aws/aws-sdk-go-v2/credentials v1.19.24 h1:2hQqYCV9yqyePQ9o6dCrZc/zO8U3TwPr9mIKlZnPu/I=
github.com/aws/aws-sdk-go-v2/credentials v1.19.24/go.mod h1:IDwpACtwqHLISdzfwUUNq4P9DsB/h5BLg4FwJPNfqFY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.5 h1:mY0qCJuWfbRxok5sRkGxehMGshSYAVIskDvPE4zIZwM=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.5/go.mod h1:pYrBdL1tMTZO7PaKRsa1cTUB8HtQh3fFM3zJHGhTQcE=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.4 h1:Uu+wqrOXozYYvaxcNIqjFsMTjoIJIZDN3R0f70ZIjyQ=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.4/go.mod h1:pYrBdL1tMTZO7PaKRsa1cTUB8HtQh3fFM3zJHGhTQcE=
github.com/aws/smithy-go v1.27.2 h1:y9NPmSE6am6LjEFPfqHqG/jJk7AauQvhCJONKh7kpzk=
github.com/aws/smithy-go v1.27.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
@@ -413,6 +413,8 @@ github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pw
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/graph-gophers/graphql-go v1.10.2 h1:HXu6Wu5klCH4ALn1fQHVI20cjEIa4wftavHIgbLA4Fo=
github.com/graph-gophers/graphql-go v1.10.2/go.mod h1:AsADheC4CCFwd8n1/QbkduTlHgYYMsRgtPihYVAlEsk=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -469,12 +471,12 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ=
github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
@@ -532,8 +534,8 @@ github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.2.1 h1:PfBfwvKB/MmqyN8Vb1G9voWisaM9OrLv+WwOvMwS9Dw=
github.com/minio/minio-go/v7 v7.2.1/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
github.com/minio/minio-go/v7 v7.2.0 h1:RCJM0R1XOsRs+A3x3UCaf3ZYbByDaLjFeAi+YCQEPhs=
github.com/minio/minio-go/v7 v7.2.0/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
github.com/minio/minlz v1.1.1 h1:OGmft1V6AnI/Wme332U6bhG54nxEan+VFgkD7lat4KM=
github.com/minio/minlz v1.1.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -707,8 +709,8 @@ github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/urfave/cli-docs/v3 v3.1.0 h1:Sa5xm19IpE5gpm6tZzXdfjdFxn67PnEsE4dpXF7vsKw=
github.com/urfave/cli-docs/v3 v3.1.0/go.mod h1:59d+5Hz1h6GSGJ10cvcEkbIe3j233t4XDqI72UIx7to=
github.com/urfave/cli/v3 v3.10.1 h1:7Kx9H50hrHbRbyxgO1KP6/BcbiGRz0uYh5YyQ30JEEY=
github.com/urfave/cli/v3 v3.10.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/urfave/cli/v3 v3.10.0 h1:0aU8yOObVDMkM13Cj4G+zb4P0PdeJMec65f81Ak1ioM=
github.com/urfave/cli/v3 v3.10.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/wneessen/go-mail v0.7.3 h1:g3DravXC5SMlVdboFrQA8Jx95A8sOzoBeS5F+vzNRK0=
github.com/wneessen/go-mail v0.7.3/go.mod h1:QGhBX0yNbc1J+Mkjcu7z2rpj4B4l+BmDY8gYznPC9sk=
@@ -738,8 +740,8 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
gitlab.com/gitlab-org/api/client-go/v2 v2.44.0 h1:Vtv2WKC8p9BAygu5VCZlZUwDhTQ7UMlS3PErXyjUmeY=
gitlab.com/gitlab-org/api/client-go/v2 v2.44.0/go.mod h1:pTbeBowtVA+0/ZExWEZYUGOrpu5qlRN5ZyOUf27BnVY=
gitlab.com/gitlab-org/api/client-go/v2 v2.42.0 h1:Bq5YIYgUJVbt4Hbh7ibBwNR4SNEafsyDVhIXl7dXDdg=
gitlab.com/gitlab-org/api/client-go/v2 v2.42.0/go.mod h1:SKUbKSS59KPt6WeGNJoYF8HDaf/rFMUSITlftj/HkLg=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
@@ -896,8 +898,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad h1:45WmJvIV6C2+O/jjLkPUH+F3aOj/1miDoU2DD0+NWbg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU=
google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -962,5 +964,5 @@ strk.kbt.io/projects/go/libravatar v0.0.0-20260301104140-add494e31dab h1:3IZDVyI
strk.kbt.io/projects/go/libravatar v0.0.0-20260301104140-add494e31dab/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo=
xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.4.1 h1:m7QlNd0eBGb31IV4Q/ow0Du83rtdC1CiwlvJZGvYde8=
xorm.io/xorm v1.4.1/go.mod h1:cs0ePc8O4a0jD78cNvD+0VFwhqotTvLQZv372QsDw7Q=
xorm.io/xorm v1.3.11 h1:i4tlVUASogb0ZZFJHA7dZqoRU2pUpUsutnNdaOlFyMI=
xorm.io/xorm v1.3.11/go.mod h1:cs0ePc8O4a0jD78cNvD+0VFwhqotTvLQZv372QsDw7Q=

View File

@@ -135,8 +135,8 @@ type StatusInfo struct {
// GetStatusInfoList returns a slice of StatusInfo
func GetStatusInfoList(ctx context.Context, lang translation.Locale) []StatusInfo {
// same as those in aggregateJobStatus (StatusUnknown excluded; it's the "shouldn't happen" fallback)
allStatus := []Status{StatusSuccess, StatusFailure, StatusCancelled, StatusSkipped, StatusWaiting, StatusRunning, StatusBlocked, StatusCancelling}
// same as those in aggregateJobStatus
allStatus := []Status{StatusSuccess, StatusFailure, StatusWaiting, StatusRunning, StatusCancelling}
statusInfoList := make([]StatusInfo, 0, len(allStatus))
for _, s := range allStatus {
statusInfoList = append(statusInfoList, StatusInfo{

View File

@@ -73,11 +73,8 @@ func TestGetStatusInfoList(t *testing.T) {
assert.Equal(t, []StatusInfo{
{Status: int(StatusSuccess), StatusName: StatusSuccess.String(), DisplayedStatus: "actions.status.success"},
{Status: int(StatusFailure), StatusName: StatusFailure.String(), DisplayedStatus: "actions.status.failure"},
{Status: int(StatusCancelled), StatusName: StatusCancelled.String(), DisplayedStatus: "actions.status.cancelled"},
{Status: int(StatusSkipped), StatusName: StatusSkipped.String(), DisplayedStatus: "actions.status.skipped"},
{Status: int(StatusWaiting), StatusName: StatusWaiting.String(), DisplayedStatus: "actions.status.waiting"},
{Status: int(StatusRunning), StatusName: StatusRunning.String(), DisplayedStatus: "actions.status.running"},
{Status: int(StatusBlocked), StatusName: StatusBlocked.String(), DisplayedStatus: "actions.status.blocked"},
{Status: int(StatusCancelling), StatusName: StatusCancelling.String(), DisplayedStatus: "actions.status.cancelling"},
}, statusInfoList)
}

View File

@@ -251,24 +251,21 @@ func (opts FindRunnerOptions) ToConds() builder.Cond {
}
func (opts FindRunnerOptions) ToOrders() string {
// A unique tiebreaker (id) is appended so that runners sharing the same
// last_online or name keep a deterministic order across paginated queries,
// otherwise the same runner may appear on more than one page.
switch opts.Sort {
case "online":
return "last_online DESC, id ASC"
return "last_online DESC"
case "offline":
return "last_online ASC, id ASC"
return "last_online ASC"
case "alphabetically":
return "name ASC, id ASC"
return "name ASC"
case "reversealphabetically":
return "name DESC, id ASC"
return "name DESC"
case "newest":
return "id DESC"
case "oldest":
return "id ASC"
}
return "last_online DESC, id ASC"
return "last_online DESC"
}
// GetRunnerByUUID returns a runner via uuid

View File

@@ -1,77 +0,0 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"fmt"
"testing"
"gitea.dev/models/db"
"gitea.dev/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFindRunnerOptions_ToOrders_StableTiebreaker(t *testing.T) {
// Sorts on a non-unique column must end with the unique id tiebreaker so
// pagination is deterministic; without it, runners sharing the same
// last_online or name can appear on more than one page. Sorts already on
// the unique id need no tiebreaker.
expected := map[string]string{
"": "last_online DESC, id ASC",
"online": "last_online DESC, id ASC",
"offline": "last_online ASC, id ASC",
"alphabetically": "name ASC, id ASC",
"reversealphabetically": "name DESC, id ASC",
"newest": "id DESC",
"oldest": "id ASC",
}
for sort, want := range expected {
assert.Equal(t, want, FindRunnerOptions{Sort: sort}.ToOrders(), "sort %q", sort)
}
}
func TestFindRunners_PaginationNoDuplicates(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
ctx := t.Context()
// Create several runners that all share the same last_online value so the
// primary sort key (last_online) is tied for all of them.
const ownerID = 1000
const count = 6
for i := range count {
runner := &ActionRunner{
Name: "paginated-runner",
UUID: fmt.Sprintf("PAGINATE-TEST-0000-0000-00000000000%d", i),
TokenHash: fmt.Sprintf("paginate-test-token-hash-%d", i),
OwnerID: ownerID,
RepoID: 0,
LastOnline: 42,
}
require.NoError(t, db.Insert(ctx, runner))
}
// Page through the runners and ensure every id is returned exactly once.
seen := make(map[int64]int)
const pageSize = 2
for page := 1; ; page++ {
runners, err := db.Find[ActionRunner](ctx, FindRunnerOptions{
ListOptions: db.ListOptions{Page: page, PageSize: pageSize},
OwnerID: ownerID,
})
require.NoError(t, err)
if len(runners) == 0 {
break
}
for _, r := range runners {
seen[r.ID]++
}
}
assert.Len(t, seen, count, "each runner should be returned exactly once across all pages")
for id, n := range seen {
assert.Equal(t, 1, n, "runner %d appeared on %d pages", id, n)
}
}

View File

@@ -195,18 +195,3 @@ func HasTwoFactorOrWebAuthn(ctx context.Context, id int64) (bool, error) {
}
return HasWebAuthnRegistrationsByUID(ctx, id)
}
// DisableTwoFactor removes every two-factor method of the given user atomically,
// returning the number of TOTP records and WebAuthn credentials removed.
// It is a no-op for a user that has no 2FA enrolled.
func DisableTwoFactor(ctx context.Context, uid int64) (totp, webAuthn int64, err error) {
err = db.WithTx(ctx, func(ctx context.Context) error {
var e error
if totp, e = db.GetEngine(ctx).Where("uid = ?", uid).Delete(&TwoFactor{}); e != nil {
return e
}
webAuthn, e = db.GetEngine(ctx).Where("user_id = ?", uid).Delete(&WebAuthnCredential{})
return e
})
return totp, webAuthn, err
}

View File

@@ -10,7 +10,6 @@ import (
auth_model "gitea.dev/models/auth"
"gitea.dev/models/unittest"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/pquerna/otp/totp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -46,37 +45,3 @@ func TestTwoFactorValidateAndConsumeTOTP(t *testing.T) {
require.NoError(t, err)
assert.False(t, ok)
}
func TestDisableTwoFactor(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
ctx := t.Context()
const uid = 1000 // a uid with no user/2FA fixtures
// Enroll TOTP and register a WebAuthn credential.
tfa := &auth_model.TwoFactor{UID: uid}
require.NoError(t, tfa.SetSecret("test-secret"))
require.NoError(t, auth_model.NewTwoFactor(ctx, tfa))
_, err := auth_model.CreateCredential(ctx, uid, "test-key", &webauthn.Credential{ID: []byte("test-cred-id")})
require.NoError(t, err)
has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, uid)
require.NoError(t, err)
require.True(t, has)
// Both records are removed and counted separately.
totp, webAuthn, err := auth_model.DisableTwoFactor(ctx, uid)
require.NoError(t, err)
assert.EqualValues(t, 1, totp)
assert.EqualValues(t, 1, webAuthn)
has, err = auth_model.HasTwoFactorOrWebAuthn(ctx, uid)
require.NoError(t, err)
assert.False(t, has)
// A second call on a user without 2FA is a no-op.
totp, webAuthn, err = auth_model.DisableTwoFactor(ctx, uid)
require.NoError(t, err)
assert.EqualValues(t, 0, totp)
assert.EqualValues(t, 0, webAuthn)
}

View File

@@ -55,34 +55,29 @@ func ParseScopedWorkflows(sourceCommit *git.Commit) ([]*ParsedScopedWorkflow, er
return parsed, nil
}
// MatchScopedWorkflows evaluates already-parsed scoped workflows against one consuming event.
// It returns the workflows whose `on:` matches, and those that matched the event but were excluded by a branch/paths filter (filtered).
// MatchScopedWorkflows evaluates already-parsed scoped workflows against one consuming event, returning those whose `on:` matches.
func MatchScopedWorkflows(
parsed []*ParsedScopedWorkflow,
consumerGitRepo *git.Repository,
consumerCommit *git.Commit,
triggedEvent webhook_module.HookEventType,
payload api.Payloader,
) (matched, filtered []*DetectedWorkflow) {
) []*DetectedWorkflow {
workflows := make([]*DetectedWorkflow, 0, len(parsed))
for _, p := range parsed {
for _, evt := range p.Events {
if evt.IsSchedule() {
// schedule is a non-target for scoped workflows
continue
}
dwf := &DetectedWorkflow{
EntryName: p.EntryName,
TriggerEvent: evt,
Content: p.Content,
}
switch detectWorkflowMatch(consumerGitRepo, consumerCommit, triggedEvent, payload, evt) {
case detectMatched:
matched = append(matched, dwf)
case detectFilteredOut:
filtered = append(filtered, dwf)
case detectNotApplicable:
if detectMatched(consumerGitRepo, consumerCommit, triggedEvent, payload, evt) {
workflows = append(workflows, &DetectedWorkflow{
EntryName: p.EntryName,
TriggerEvent: evt,
Content: p.Content,
})
}
}
}
return matched, filtered
return workflows
}

View File

@@ -30,14 +30,6 @@ type DetectedWorkflow struct {
Content []byte
}
type detectResult int
const (
detectMatched detectResult = iota // event matched; run normally
detectNotApplicable // event/type doesn't apply; create nothing
detectFilteredOut // matched but excluded by a branch/paths filter; emits a skipped commit status
)
func init() {
model.OnDecodeNodeError = func(node yaml.Node, out any, err error) {
// Log the error instead of panic or fatal.
@@ -180,16 +172,18 @@ func DetectWorkflows(
triggedEvent webhook_module.HookEventType,
payload api.Payloader,
detectSchedule bool,
) (workflows, schedules, filtered []*DetectedWorkflow, err error) {
) ([]*DetectedWorkflow, []*DetectedWorkflow, error) {
_, entries, err := ListWorkflows(commit)
if err != nil {
return nil, nil, nil, err
return nil, nil, err
}
workflows := make([]*DetectedWorkflow, 0, len(entries))
schedules := make([]*DetectedWorkflow, 0, len(entries))
for _, entry := range entries {
content, err := GetContentFromEntry(entry)
if err != nil {
return nil, nil, nil, err
return nil, nil, err
}
// one workflow may have multiple events
@@ -209,24 +203,18 @@ func DetectWorkflows(
}
schedules = append(schedules, dwf)
}
} else {
} else if detectMatched(gitRepo, commit, triggedEvent, payload, evt) {
dwf := &DetectedWorkflow{
EntryName: entry.Name(),
TriggerEvent: evt,
Content: content,
}
switch detectWorkflowMatch(gitRepo, commit, triggedEvent, payload, evt) {
case detectMatched:
workflows = append(workflows, dwf)
case detectFilteredOut:
filtered = append(filtered, dwf)
case detectNotApplicable:
}
workflows = append(workflows, dwf)
}
}
}
return workflows, schedules, filtered, nil
return workflows, schedules, nil
}
func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*DetectedWorkflow, error) {
@@ -264,9 +252,9 @@ func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*D
return wfs, nil
}
func detectWorkflowMatch(gitRepo *git.Repository, commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) detectResult {
func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool {
if !canGithubEventMatch(evt.Name, triggedEvent) {
return detectNotApplicable
return false
}
switch triggedEvent {
@@ -280,7 +268,7 @@ func detectWorkflowMatch(gitRepo *git.Repository, commit *git.Commit, triggedEve
log.Warn("Ignore unsupported %s event arguments %v", triggedEvent, evt.Acts())
}
// no special filter parameters for these events, just return true if name matched
return detectMatched
return true
case // push
webhook_module.HookEventPush:
@@ -291,20 +279,14 @@ func detectWorkflowMatch(gitRepo *git.Repository, commit *git.Commit, triggedEve
webhook_module.HookEventIssueAssign,
webhook_module.HookEventIssueLabel,
webhook_module.HookEventIssueMilestone:
if matchIssuesEvent(payload.(*api.IssuePayload), evt) {
return detectMatched
}
return detectNotApplicable
return matchIssuesEvent(payload.(*api.IssuePayload), evt)
case // issue_comment
webhook_module.HookEventIssueComment,
// `pull_request_comment` is same as `issue_comment`
// See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_comment-use-issue_comment
webhook_module.HookEventPullRequestComment:
if matchIssueCommentEvent(payload.(*api.IssueCommentPayload), evt) {
return detectMatched
}
return detectNotApplicable
return matchIssueCommentEvent(payload.(*api.IssueCommentPayload), evt)
case // pull_request
webhook_module.HookEventPullRequest,
@@ -318,49 +300,34 @@ func detectWorkflowMatch(gitRepo *git.Repository, commit *git.Commit, triggedEve
case // pull_request_review
webhook_module.HookEventPullRequestReviewApproved,
webhook_module.HookEventPullRequestReviewRejected:
if matchPullRequestReviewEvent(payload.(*api.PullRequestPayload), evt) {
return detectMatched
}
return detectNotApplicable
return matchPullRequestReviewEvent(payload.(*api.PullRequestPayload), evt)
case // pull_request_review_comment
webhook_module.HookEventPullRequestReviewComment:
if matchPullRequestReviewCommentEvent(payload.(*api.PullRequestPayload), evt) {
return detectMatched
}
return detectNotApplicable
return matchPullRequestReviewCommentEvent(payload.(*api.PullRequestPayload), evt)
case // release
webhook_module.HookEventRelease:
if matchReleaseEvent(payload.(*api.ReleasePayload), evt) {
return detectMatched
}
return detectNotApplicable
return matchReleaseEvent(payload.(*api.ReleasePayload), evt)
case // registry_package
webhook_module.HookEventPackage:
if matchPackageEvent(payload.(*api.PackagePayload), evt) {
return detectMatched
}
return detectNotApplicable
return matchPackageEvent(payload.(*api.PackagePayload), evt)
case // workflow_run
webhook_module.HookEventWorkflowRun:
if matchWorkflowRunEvent(payload.(*api.WorkflowRunPayload), evt) {
return detectMatched
}
return detectNotApplicable
return matchWorkflowRunEvent(payload.(*api.WorkflowRunPayload), evt)
default:
log.Warn("unsupported event %q", triggedEvent)
return detectNotApplicable
return false
}
}
func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobparser.Event) detectResult {
func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobparser.Event) bool {
// with no special filter parameters
if len(evt.Acts()) == 0 {
return detectMatched
return true
}
matchTimes := 0
@@ -426,14 +393,14 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
return detectNotApplicable
}
patterns, err := workflowpattern.CompilePatterns(vals...)
if err != nil {
break
}
if !workflowpattern.Skip(patterns, filesChanged) {
matchTimes++
} else {
patterns, err := workflowpattern.CompilePatterns(vals...)
if err != nil {
break
}
if !workflowpattern.Skip(patterns, filesChanged) {
matchTimes++
}
}
case "paths-ignore":
if refName.IsTag() {
@@ -443,14 +410,14 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
return detectNotApplicable
}
patterns, err := workflowpattern.CompilePatterns(vals...)
if err != nil {
break
}
if !workflowpattern.Filter(patterns, filesChanged) {
matchTimes++
} else {
patterns, err := workflowpattern.CompilePatterns(vals...)
if err != nil {
break
}
if !workflowpattern.Filter(patterns, filesChanged) {
matchTimes++
}
}
default:
log.Warn("push event unsupported condition %q", cond)
@@ -460,10 +427,7 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
if hasBranchFilter && hasTagFilter {
matchTimes++
}
if matchTimes == len(evt.Acts()) {
return detectMatched
}
return detectFilteredOut
return matchTimes == len(evt.Acts())
}
func matchIssuesEvent(issuePayload *api.IssuePayload, evt *jobparser.Event) bool {
@@ -514,7 +478,7 @@ func matchIssuesEvent(issuePayload *api.IssuePayload, evt *jobparser.Event) bool
return matchTimes == len(evt.Acts())
}
func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) detectResult {
func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) bool {
acts := evt.Acts()
activityTypeMatched := false
matchTimes := 0
@@ -561,7 +525,7 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa
headCommit, err = gitRepo.GetCommit(prPayload.PullRequest.Head.Sha)
if err != nil {
log.Error("GetCommit [ref: %s]: %v", prPayload.PullRequest.Head.Sha, err)
return detectNotApplicable
return false
}
}
@@ -593,39 +557,33 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa
filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.MergeBase)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err)
return detectNotApplicable
}
patterns, err := workflowpattern.CompilePatterns(vals...)
if err != nil {
break
}
if !workflowpattern.Skip(patterns, filesChanged) {
matchTimes++
} else {
patterns, err := workflowpattern.CompilePatterns(vals...)
if err != nil {
break
}
if !workflowpattern.Skip(patterns, filesChanged) {
matchTimes++
}
}
case "paths-ignore":
filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.MergeBase)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err)
return detectNotApplicable
}
patterns, err := workflowpattern.CompilePatterns(vals...)
if err != nil {
break
}
if !workflowpattern.Filter(patterns, filesChanged) {
matchTimes++
} else {
patterns, err := workflowpattern.CompilePatterns(vals...)
if err != nil {
break
}
if !workflowpattern.Filter(patterns, filesChanged) {
matchTimes++
}
}
default:
log.Warn("pull request event unsupported condition %q", cond)
}
}
if !activityTypeMatched {
return detectNotApplicable
}
if matchTimes != len(evt.Acts()) {
return detectFilteredOut
}
return detectMatched
return activityTypeMatched && matchTimes == len(evt.Acts())
}
func matchIssueCommentEvent(issueCommentPayload *api.IssueCommentPayload, evt *jobparser.Event) bool {

View File

@@ -101,49 +101,49 @@ func TestDetectMatched(t *testing.T) {
triggedEvent webhook_module.HookEventType
payload api.Payloader
yamlOn string
expected detectResult
expected bool
}{
{
desc: "HookEventCreate(create) matches GithubEventCreate(create)",
triggedEvent: webhook_module.HookEventCreate,
payload: nil,
yamlOn: "on: create",
expected: detectMatched,
expected: true,
},
{
desc: "HookEventIssues(issues) `opened` action matches GithubEventIssues(issues)",
triggedEvent: webhook_module.HookEventIssues,
payload: &api.IssuePayload{Action: api.HookIssueOpened},
yamlOn: "on: issues",
expected: detectMatched,
expected: true,
},
{
desc: "HookEventIssues(issues) `milestoned` action matches GithubEventIssues(issues)",
triggedEvent: webhook_module.HookEventIssues,
payload: &api.IssuePayload{Action: api.HookIssueMilestoned},
yamlOn: "on: issues",
expected: detectMatched,
expected: true,
},
{
desc: "HookEventPullRequestSync(pull_request_sync) matches GithubEventPullRequest(pull_request)",
triggedEvent: webhook_module.HookEventPullRequestSync,
payload: &api.PullRequestPayload{Action: api.HookIssueSynchronized},
yamlOn: "on: pull_request",
expected: detectMatched,
expected: true,
},
{
desc: "HookEventPullRequest(pull_request) `label_updated` action doesn't match GithubEventPullRequest(pull_request) with no activity type",
triggedEvent: webhook_module.HookEventPullRequest,
payload: &api.PullRequestPayload{Action: api.HookIssueLabelUpdated},
yamlOn: "on: pull_request",
expected: detectNotApplicable,
expected: false,
},
{
desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with no activity type",
triggedEvent: webhook_module.HookEventPullRequest,
payload: &api.PullRequestPayload{Action: api.HookIssueClosed},
yamlOn: "on: pull_request",
expected: detectNotApplicable,
expected: false,
},
{
desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with branches",
@@ -155,56 +155,56 @@ func TestDetectMatched(t *testing.T) {
},
},
yamlOn: "on:\n pull_request:\n branches: [main]",
expected: detectNotApplicable,
expected: false,
},
{
desc: "HookEventPullRequest(pull_request) `label_updated` action matches GithubEventPullRequest(pull_request) with `label` activity type",
triggedEvent: webhook_module.HookEventPullRequest,
payload: &api.PullRequestPayload{Action: api.HookIssueLabelUpdated},
yamlOn: "on:\n pull_request:\n types: [labeled]",
expected: detectMatched,
expected: true,
},
{
desc: "HookEventPullRequestReviewComment(pull_request_review_comment) matches GithubEventPullRequestReviewComment(pull_request_review_comment)",
triggedEvent: webhook_module.HookEventPullRequestReviewComment,
payload: &api.PullRequestPayload{Action: api.HookIssueReviewed},
yamlOn: "on:\n pull_request_review_comment:\n types: [created]",
expected: detectMatched,
expected: true,
},
{
desc: "HookEventPullRequestReviewRejected(pull_request_review_rejected) doesn't match GithubEventPullRequestReview(pull_request_review) with `dismissed` activity type (we don't support `dismissed` at present)",
triggedEvent: webhook_module.HookEventPullRequestReviewRejected,
payload: &api.PullRequestPayload{Action: api.HookIssueReviewed},
yamlOn: "on:\n pull_request_review:\n types: [dismissed]",
expected: detectNotApplicable,
expected: false,
},
{
desc: "HookEventRelease(release) `published` action matches GithubEventRelease(release) with `published` activity type",
triggedEvent: webhook_module.HookEventRelease,
payload: &api.ReleasePayload{Action: api.HookReleasePublished},
yamlOn: "on:\n release:\n types: [published]",
expected: detectMatched,
expected: true,
},
{
desc: "HookEventPackage(package) `created` action doesn't match GithubEventRegistryPackage(registry_package) with `updated` activity type",
triggedEvent: webhook_module.HookEventPackage,
payload: &api.PackagePayload{Action: api.HookPackageCreated},
yamlOn: "on:\n registry_package:\n types: [updated]",
expected: detectNotApplicable,
expected: false,
},
{
desc: "HookEventWiki(wiki) matches GithubEventGollum(gollum)",
triggedEvent: webhook_module.HookEventWiki,
payload: nil,
yamlOn: "on: gollum",
expected: detectMatched,
expected: true,
},
{
desc: "HookEventSchedule(schedule) matches GithubEventSchedule(schedule)",
triggedEvent: webhook_module.HookEventSchedule,
payload: nil,
yamlOn: "on: schedule",
expected: detectMatched,
expected: true,
},
{
desc: "push to tag matches workflow with paths condition (should skip paths check)",
@@ -222,19 +222,7 @@ func TestDetectMatched(t *testing.T) {
},
commit: nil,
yamlOn: "on:\n push:\n paths:\n - src/**",
expected: detectMatched,
},
{
desc: "push branch filter excludes -> filtered out",
triggedEvent: webhook_module.HookEventPush,
payload: &api.PushPayload{
Ref: "refs/heads/feature/x",
Before: "0000000",
Commits: []*api.PayloadCommit{{ID: "abc", Added: []string{"a.go"}, Message: "x"}},
},
commit: nil,
yamlOn: "on:\n push:\n branches: [main]",
expected: detectFilteredOut,
expected: true,
},
}
@@ -243,7 +231,7 @@ func TestDetectMatched(t *testing.T) {
evts, err := GetEventsFromContent(fullWorkflowContent(tc.yamlOn))
assert.NoError(t, err)
assert.Len(t, evts, 1)
assert.Equal(t, tc.expected, detectWorkflowMatch(nil, tc.commit, tc.triggedEvent, tc.payload, evts[0]))
assert.Equal(t, tc.expected, detectMatched(nil, tc.commit, tc.triggedEvent, tc.payload, evts[0]))
})
}
}

View File

@@ -21,6 +21,19 @@ func TestCommitsCount(t *testing.T) {
assert.Equal(t, int64(3), commitsCount)
}
func TestCommitsCountWithoutBase(t *testing.T) {
bareRepo1 := &mockRepository{path: "repo1_bare"}
commitsCount, err := CommitsCount(t.Context(), bareRepo1,
CommitsCountOptions{
Not: "master",
Revision: []string{"branch1"},
})
assert.NoError(t, err)
assert.Equal(t, int64(2), commitsCount)
}
func TestCommitsCountWithSinceUntil(t *testing.T) {
bareRepo1 := &mockRepository{path: "repo1_bare"}
revision := []string{"8006ff9adbf0cb94da7dad9e537e53817f9fa5c0"}
@@ -52,19 +65,6 @@ func TestCommitsCountWithSinceUntil(t *testing.T) {
}
}
func TestCommitsCountWithoutBase(t *testing.T) {
bareRepo1 := &mockRepository{path: "repo1_bare"}
commitsCount, err := CommitsCount(t.Context(), bareRepo1,
CommitsCountOptions{
Not: "master",
Revision: []string{"branch1"},
})
assert.NoError(t, err)
assert.Equal(t, int64(2), commitsCount)
}
func TestGetLatestCommitTime(t *testing.T) {
bareRepo1 := &mockRepository{path: "repo1_bare"}
lct, err := GetLatestCommitTime(t.Context(), bareRepo1)

View File

@@ -54,7 +54,7 @@ func (st *Sanitizer) createDefaultPolicy() *bluemonday.Policy {
// Allow 'color' and 'background-color' properties for the style attribute on text elements.
policy.AllowStyles("color", "background-color").OnElements("div", "span", "p", "tr", "th", "td")
policy.AllowAttrs("src", "autoplay", "controls", "muted", "loop", "playsinline").OnElements("video")
policy.AllowAttrs("src", "autoplay", "controls").OnElements("video")
// Native support of "<picture><source media=... srcset=...><img src=...></picture>"
// ATTENTION: it only works with "auto" theme, because "media" query doesn't work with the theme chosen by end user manually.

View File

@@ -73,18 +73,17 @@ func TestInitKeys(t *testing.T) {
require.NoError(t, err)
assert.Len(t, keyFiles, len(keyTypes))
// Record file contents so regeneration can be detected
content := map[string][]byte{}
metadata := map[string]os.FileInfo{}
for _, keyType := range keyTypes {
privKeyPath := filepath.Join(tempDir, "gitea."+keyType)
pubKeyPath := filepath.Join(tempDir, "gitea."+keyType+".pub")
data, err := os.ReadFile(privKeyPath)
info, err := os.Stat(privKeyPath)
require.NoError(t, err)
content[privKeyPath] = data
metadata[privKeyPath] = info
data, err = os.ReadFile(pubKeyPath)
info, err = os.Stat(pubKeyPath)
require.NoError(t, err)
content[pubKeyPath] = data
metadata[pubKeyPath] = info
}
// Test recreation on missing private key and noop for missing pub key
@@ -99,26 +98,26 @@ func TestInitKeys(t *testing.T) {
privKeyPath := filepath.Join(tempDir, "gitea."+keyType)
pubKeyPath := filepath.Join(tempDir, "gitea."+keyType+".pub")
dataPriv, err := os.ReadFile(privKeyPath)
infoPriv, err := os.Stat(privKeyPath)
require.NoError(t, err)
switch keyType {
case "rsa":
// No modification to RSA key
dataPub, err := os.ReadFile(pubKeyPath)
infoPub, err := os.Stat(pubKeyPath)
require.NoError(t, err)
assert.Equal(t, content[privKeyPath], dataPriv)
assert.Equal(t, content[pubKeyPath], dataPub)
assert.Equal(t, metadata[privKeyPath], infoPriv)
assert.Equal(t, metadata[pubKeyPath], infoPub)
case "ecdsa":
// ECDSA public key should be missing, private unchanged
assert.Equal(t, content[privKeyPath], dataPriv)
assert.Equal(t, metadata[privKeyPath], infoPriv)
assert.NoFileExists(t, pubKeyPath)
case "ed25519":
// ed25519 private key was removed, so both keys regenerated
dataPub, err := os.ReadFile(pubKeyPath)
infoPub, err := os.Stat(pubKeyPath)
require.NoError(t, err)
assert.NotEqual(t, content[privKeyPath], dataPriv)
assert.NotEqual(t, content[pubKeyPath], dataPub)
assert.NotEqual(t, metadata[privKeyPath], infoPriv)
assert.NotEqual(t, metadata[pubKeyPath], infoPub)
}
}
}

View File

@@ -19,6 +19,7 @@ type CreateUserOption struct {
// The full display name of the user
FullName string `json:"full_name" binding:"MaxSize(100)"`
// required: true
// swagger:strfmt email
Email string `json:"email" binding:"Required;Email;MaxSize(254)"`
// The plain text password for the user
Password string `json:"password" binding:"MaxSize(255)"`

View File

@@ -133,6 +133,7 @@ type IssueAssigneesOption struct {
// EditDeadlineOption options for creating a deadline
type EditDeadlineOption struct {
// required:true
// swagger:strfmt date-time
Deadline *time.Time `json:"due_date"`
}

View File

@@ -54,10 +54,10 @@ type CreateTeamOption struct {
// Whether the team has access to all repositories in the organization
IncludesAllRepositories bool `json:"includes_all_repositories"`
Permission RepoWritePermission `json:"permission"`
// example: ["repo.actions","repo.packages","repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
// example: ["repo.actions","repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.ext_wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
// Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.
Units []string `json:"units"`
// example: {"repo.actions":"read","repo.packages":"read","repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"}
// example: {"repo.actions","repo.packages","repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"}
UnitsMap map[string]string `json:"units_map"`
// Whether the team can create repositories in the organization
CanCreateOrgRepo bool `json:"can_create_org_repo"`

View File

@@ -74,7 +74,7 @@
"forks": "Forks",
"activities": "Aktivitäten",
"pull_requests": "Pull-Requests",
"issues": "Issues",
"issues": "Probleme",
"milestones": "Meilensteine",
"ok": "OK",
"cancel": "Abbrechen",
@@ -1101,7 +1101,7 @@
"repo.migrate_items_wiki": "Wiki",
"repo.migrate_items_milestones": "Meilensteine",
"repo.migrate_items_labels": "Labels",
"repo.migrate_items_issues": "Issues",
"repo.migrate_items_issues": "Probleme",
"repo.migrate_items_pullrequests": "Pull-Requests",
"repo.migrate_items_merge_requests": "Merge-Requests",
"repo.migrate_items_releases": "Veröffentlichungen",
@@ -1178,7 +1178,7 @@
"repo.find_tag": "Tag finden",
"repo.branches": "Branches",
"repo.tags": "Tags",
"repo.issues": "Issues",
"repo.issues": "Probleme",
"repo.pulls": "Pull-Requests",
"repo.projects": "Projekte",
"repo.packages": "Pakete",
@@ -2300,7 +2300,7 @@
"repo.settings.event_repository": "Repository",
"repo.settings.event_repository_desc": "Repository erstellt oder gelöscht.",
"repo.settings.event_header_issue": "Issue Ereignisse",
"repo.settings.event_issues": "Issues",
"repo.settings.event_issues": "Probleme",
"repo.settings.event_issues_desc": "Issue geöffnet, geschlossen, wieder geöffnet, bearbeitet oder gelöscht.",
"repo.settings.event_issue_assign": "Issue zugewiesen",
"repo.settings.event_issue_assign_desc": "Issue zugewiesen oder Zuweisung entfernt.",
@@ -3104,7 +3104,7 @@
"admin.repos.owner": "Besitzer",
"admin.repos.name": "Name",
"admin.repos.private": "Privat",
"admin.repos.issues": "Issues",
"admin.repos.issues": "Probleme",
"admin.repos.size": "Größe",
"admin.repos.lfs_size": "LFS-Größe",
"admin.packages.package_manage_panel": "Paketverwaltung",
@@ -3779,7 +3779,6 @@
"actions.runs.commit": "Commit",
"actions.runs.run_details": "Run Details",
"actions.runs.workflow_file": "Workflow-Datei",
"actions.runs.workflow_file_no_permission": "Keine Berechtigung zum Anzeigen der Workflow-Datei",
"actions.runs.scheduled": "Geplant",
"actions.runs.pushed_by": "gepusht von",
"actions.runs.invalid_workflow_helper": "Die Workflow-Konfigurationsdatei ist ungültig. Bitte überprüfe Deine Konfigurationsdatei: %s",
@@ -3836,33 +3835,7 @@
"actions.workflow.scope_owner": "Besitzer",
"actions.workflow.scope_global": "Global",
"actions.workflow.required": "Erforderlich",
"actions.workflow.scoped_required_cannot_disable": "Dieser Scoped Workflow ist erforderlich und kann nicht deaktiviert werden.",
"actions.scoped_workflows": "Scoped Workflows",
"actions.scoped_workflows.desc_org": "Repositories als Scoped Workflow Quellen registrieren. Workflow-Dateien unter den Workflow-Verzeichnissen eines Quell-Repositorys laufen in jedem Projektarchiv dieser Organisation, im eigenen Kontext des Projektarchivs.",
"actions.scoped_workflows.desc_user": "Repositories als Scoped Workflow Quellen registrieren. Workflow-Dateien unter den Workflow-Verzeichnissen eines Quell-Repositorys laufen in jedem Projektarchiv dieser Organisation, im eigenen Kontext des Projektarchivs.",
"actions.scoped_workflows.desc_global": "Repositories als Scoped Workflow-Quellen registrieren. Workflow-Dateien unter den Workflow-Verzeichnissen eines Quellcode-Repositorys laufen auf jedem Projektarchiv in dieser Instanz im eigenen Kontext des Projektarchivs. Da Quellen auf Instanzenebene auf den Ereignissen jedes Projektarchivs ausgewertet werden, kann die Registrierung bei großen Instanzen Overhead hinzufügen.",
"actions.scoped_workflows.add_help": "Um Scoped Workflows aus einem Repository zu erstellen, übertrage die Workflow-Dateien unter <code>%s</code> in seinem Standard Branch, dann registrieren Sie das Projektarchiv als Quelle unten.",
"actions.scoped_workflows.security_note": "Der Workflow-Inhalt eines Quell-Repositorys wird in jedem Repository ausgeführt, für das er gilt. Die Skripte der einzelnen Schritte sowie deren Ausgabe werden in den Actions-Logs des jeweiligen Repositorys gespeichert und sind für alle sichtbar, die die Actions des konsumierenden Repositorys einsehen können. Das Registrieren eines privaten Repositorys als Quelle legt daher dessen Workflow-Logik über diese Logs offen. Registriere nur Repositorys, deren Workflow-Inhalte mit allen konsumierenden Repositorys geteilt werden dürfen. Wenn ein scoped Workflow einen wiederverwendbaren Workflow aus einem privaten Repository referenziert, stelle sicher, dass jedes konsumierende Repository darauf Lesezugriff hat andernfalls schlägt der Workflow dort fehl.",
"actions.scoped_workflows.source.add": "Quell-Repository hinzufügen",
"actions.scoped_workflows.source.add_success": "Quell-Repository hinzugefügt.",
"actions.scoped_workflows.source.remove_success": "Quell-Repository entfernt.",
"actions.scoped_workflows.source.not_found": "Repository nicht gefunden.",
"actions.scoped_workflows.required.update_success": "Erforderliche Workflows aktualisiert.",
"actions.scoped_workflows.required.label": "Workflows als erforderlich markieren (ein erforderlicher Workflow kann nicht durch Repositories deaktiviert werden):",
"actions.scoped_workflows.required.patterns": "Erforderliche Statusüberprüfungsmuster",
"actions.scoped_workflows.required.patterns_aria": "Erforderliche Statusüberprüfungsmuster für %s",
"actions.scoped_workflows.required.patterns_note": "nur erzwungen während der Workflow benötigt wird",
"actions.scoped_workflows.required.patterns_hint": "Markieren Sie den Workflow als erforderlich, um seine Statusüberprüfungsmuster zu konfigurieren.",
"actions.scoped_workflows.required.patterns_help": "Ein Statusüberprüfungsmuster (glob) pro Zeile. Ein Pull-Request kann erst zusammengeführt werden, wenn ein Status mit jedem Muster übereinstimmt. Dies wird für jeden Zielzweig erzwungen, der eine Schutzregel hat, auch für einen mit einer eigenen Statusüberprüfung; ein Zielzweig ohne Schutzregel ist nicht ausgeschaltet.",
"actions.scoped_workflows.required.patterns_empty": "Jeder benötigte Workflow benötigt mindestens ein Statusüberprüfungsmuster.",
"actions.scoped_workflows.required.missing_file": "nicht mehr im Quelltext",
"actions.scoped_workflows.required.expected_contexts": "Erwartete Statusüberprüfung (eine Prüfung, die zu einem Muster passt)",
"actions.scoped_workflows.required.no_status_contexts": "Dieser Workflow postet keine Status Checks, ihn als Anforderung zu markieren würde jede Pull Request blockieren. Deaktiviere die Anforderung.",
"actions.scoped_workflows.no_files": "Im Standard-Branch wurden keine Scoped Workflow-Dateien gefunden.",
"actions.workflow.run": "Workflow ausführen",
"actions.workflow.create_status_badge": "Status Badge erstellen",
"actions.workflow.status_badge": "Status Badge",
"actions.workflow.status_badge_url": "Badge-URL",
"actions.workflow.not_found": "Workflow '%s' wurde nicht gefunden.",
"actions.workflow.run_success": "Workflow '%s' erfolgreich ausgeführt.",
"actions.workflow.from_ref": "Nutze Workflow von",

View File

@@ -10,7 +10,7 @@
"sign_out": "Déconnexion",
"sign_up": "S'inscrire",
"link_account": "Lier un Compte",
"register": "Sinscrire",
"register": "S'inscrire",
"version": "Version",
"powered_by": "Propulsé par %s",
"page": "Page",
@@ -80,8 +80,8 @@
"cancel": "Annuler",
"retry": "Réessayez",
"rerun": "Relancer",
"rerun_all": "Relancer toutes les missions.",
"rerun_failed": "Relancer les missions échouées.",
"rerun_all": "Relancer toutes les tâches",
"rerun_failed": "Relancer les tâches échouées",
"save": "Enregistrer",
"add": "Ajouter",
"add_all": "Tout Ajouter",
@@ -165,7 +165,7 @@
"search.fuzzy_tooltip": "Inclure également les résultats proches de la recherche",
"search.words": "Mots",
"search.words_tooltip": "Inclure uniquement les résultats qui correspondent exactement aux mots recherchés",
"search.regexp": "Expression régulière",
"search.regexp": "Regexp",
"search.regexp_tooltip": "Inclure uniquement les résultats qui correspondent à lexpression régulière recherchée",
"search.exact": "Exact",
"search.exact_tooltip": "Inclure uniquement les résultats qui correspondent exactement au terme de recherche",
@@ -185,7 +185,7 @@
"search.tag_kind": "Chercher des étiquettes…",
"search.tag_tooltip": "Cherchez des étiquettes correspondantes. Utilisez « % » pour rechercher nimporte quelle suite de nombres.",
"search.commit_kind": "Chercher des révisions…",
"search.runner_kind": "Chercher des opérateurs…",
"search.runner_kind": "Chercher des exécuteurs…",
"search.no_results": "Aucun résultat correspondant trouvé.",
"search.issue_kind": "Recherche de tickets…",
"search.pull_kind": "Recherche de demandes dajouts…",
@@ -289,7 +289,7 @@
"install.smtp_port": "Port SMTP",
"install.smtp_from": "Envoyer les courriels en tant que",
"install.smtp_from_invalid": "Ladresse « Envoyer le courriel sous » est invalide",
"install.smtp_from_helper": "Adresse courriel utilisée par Gitea. Utilisez directement votre adresse ou la forme « Nom <courriel@exemple.com> ».",
"install.smtp_from_helper": "Adresse courriel utilisée par Gitea. Utilisez directement votre adresse ou la forme « Nom <email@example.com> ».",
"install.mailer_user": "Utilisateur SMTP",
"install.mailer_password": "Mot de passe SMTP",
"install.register_confirm": "Exiger la confirmation du courriel lors de linscription",
@@ -308,7 +308,7 @@
"install.require_sign_in_view_popup": "Limiter laccès aux pages aux utilisateurs connectés. Les visiteurs ne verront que les pages de connexion et dinscription.",
"install.admin_setting_desc": "La création d'un compte administrateur est facultative. Le premier utilisateur enregistré deviendra automatiquement un administrateur le cas échéant.",
"install.admin_title": "Paramètres de compte administrateur",
"install.admin_name": "Nom de ladministrateur",
"install.admin_name": "Nom dutilisateur administrateur",
"install.admin_password": "Mot de passe",
"install.confirm_password": "Confirmez le mot de passe",
"install.admin_email": "Courriel",
@@ -505,10 +505,10 @@
"mail.repo.actions.run.failed": "Lexécution a échoué",
"mail.repo.actions.run.succeeded": "Lexécution a réussi",
"mail.repo.actions.run.cancelled": "Lexécution a été annulée",
"mail.repo.actions.jobs.all_succeeded": "Tous les missions ont réussi.",
"mail.repo.actions.jobs.all_failed": "Toutes les missions ont échoué.",
"mail.repo.actions.jobs.some_not_successful": "Certaines missions nont pas réussi.",
"mail.repo.actions.jobs.all_cancelled": "Toutes les missions ont bien été annulées.",
"mail.repo.actions.jobs.all_succeeded": "Tous les tâches ont réussi.",
"mail.repo.actions.jobs.all_failed": "Toutes les tâches ont échoué.",
"mail.repo.actions.jobs.some_not_successful": "Certaines tâches nont pas réussi.",
"mail.repo.actions.jobs.all_cancelled": "Toutes les tâches ont bien été annulés.",
"mail.team_invite.subject": "%[1]s vous a invité à rejoindre lorganisation %[2]s",
"mail.team_invite.text_1": "%[1]s vous a invité à rejoindre léquipe %[2]s dans lorganisation %[3]s.",
"mail.team_invite.text_2": "Veuillez cliquer sur le lien suivant pour rejoindre l'équipe :",
@@ -916,7 +916,7 @@
"settings.passcode_invalid": "Le mot de passe est invalide. Réessayez.",
"settings.twofa_enrolled": "Lauthentification à deux facteurs a été activée pour votre compte. Gardez votre clé de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois.",
"settings.twofa_failed_get_secret": "Impossible d'obtenir le secret.",
"settings.webauthn_desc": "Les clés de sécurité sont des dispositifs matériels contenant des clés cryptographiques. Elles peuvent être utilisées pour lauthentification à deux facteurs. Gitea requière le support de l<a rel=\"noreferrer\" target=\"_blank\" href=\"%s\">API Web Authentication</a>.",
"settings.webauthn_desc": "Les clés de sécurité sont des dispositifs matériels contenant des clés cryptographiques. Elles peuvent être utilisées pour lauthentification à deux facteurs. La clé de sécurité doit supporter le standard <a rel=\"noreferrer\" target=\"_blank\" href=\"%s\">WebAuthn Authenticator</a>.",
"settings.webauthn_register_key": "Ajouter une clé de sécurité",
"settings.webauthn_nickname": "Pseudonyme",
"settings.webauthn_delete_key": "Retirer la clé de sécurité",
@@ -944,8 +944,8 @@
"settings.email_notifications.disable": "Ne pas notifier",
"settings.email_notifications.submit": "Définir les préférences de courriel",
"settings.email_notifications.andyourown": "Inclure vos propres notifications",
"settings.email_notifications.actions.desc": "Notifier les procédures des dépôts configurés avec les <a target=\"_blank\" href=\"%s\">Actions Gitea</a>.",
"settings.email_notifications.actions.failure_only": "Ne notifier que procédures échouées",
"settings.email_notifications.actions.desc": "Notification pour les executions de workflows sur les dépôts configurés avec les <a target=\"_blank\" href=\"%s\">Actions Gitea</a>.",
"settings.email_notifications.actions.failure_only": "Ne notifier que pour les exécutions échouées",
"settings.visibility": "Visibilité de l'utilisateur",
"settings.visibility.public": "Publique",
"settings.visibility.public_tooltip": "Visible par tout le monde",
@@ -992,7 +992,7 @@
"repo.repo_desc": "Description",
"repo.repo_desc_helper": "Décrire brièvement votre dépôt",
"repo.repo_no_desc": "Aucune description fournie",
"repo.repo_lang": "Langues",
"repo.repo_lang": "Langue",
"repo.repo_gitignore_helper": "Sélectionner quelques .gitignore prédéfinies",
"repo.repo_gitignore_helper_desc": "De nombreux outils et compilateurs génèrent des fichiers résiduels qui n'ont pas besoin d'être supervisés par git. Composez un .gitignore à laide de cette liste des languages de programmation courants.",
"repo.issue_labels": "Jeu de labels pour les tickets",
@@ -1041,7 +1041,7 @@
"repo.stars": "Favoris",
"repo.reactions_more": "et %d de plus",
"repo.reactions": "Réactions",
"repo.unit_disabled": "Ladministrateur du site a désactivé cette section du dépôt.",
"repo.unit_disabled": "L'administrateur du site a désactivé cette section du dépôt.",
"repo.language_other": "Autre",
"repo.adopt_search": "Entrez un nom dutilisateur pour rechercher les dépôts dépossédés… (laissez vide pour tous trouver)",
"repo.adopt_preexisting_label": "Adopter les fichiers",
@@ -1106,9 +1106,9 @@
"repo.migrate_items_merge_requests": "Demandes de fusion",
"repo.migrate_items_releases": "Publications",
"repo.migrate_repo": "Migrer le dépôt",
"repo.migrate.clone_address": "Migrer depuis une URL",
"repo.migrate.clone_address": "Migrer/Cloner depuis une URL",
"repo.migrate.clone_address_desc": "LURL ou le lien « Git clone » dun dépôt existant",
"repo.migrate.github_token_desc": "Vous pouvez mettre un ou plusieurs jetons séparés par des virgules ici pour rendre la migration plus rapide et contourner la limite de débit de lAPI GitHub. Attention : abuser de cette fonctionnalité peut enfreindre la politique du fournisseur de service et entraîner un blocage de votre compte.",
"repo.migrate.github_token_desc": "Vous pouvez mettre un ou plusieurs jetons séparés par des virgules ici pour rendre la migration plus rapide et contourner la limite de débit de lAPI GitHub. ATTENTION : abuser de cette fonctionnalité peut enfreindre la politique du fournisseur de service et entraîner un blocage de votre compte.",
"repo.migrate.clone_local_path": "ou un chemin serveur local",
"repo.migrate.permission_denied": "Vous n'êtes pas autorisé à importer des dépôts locaux.",
"repo.migrate.permission_denied_blocked": "Vous ne pouvez pas importer depuis des domaines bannis, veuillez demander à votre administrateur de vérifier les paramètres ALLOWED_DOMAINS, ALLOW_LOCALNETWORKS ou BLOCKED_DOMAINS.",
@@ -1165,7 +1165,7 @@
"repo.clone_this_repo": "Cloner ce dépôt",
"repo.cite_this_repo": "Citer ce dépôt",
"repo.create_new_repo_command": "Création d'un nouveau dépôt en ligne de commande",
"repo.push_exist_repo": "Soumission dun dépôt existant par ligne de commande",
"repo.push_exist_repo": "Soumission d'un dépôt existant par ligne de commande",
"repo.empty_message": "Ce dépôt na pas de contenu.",
"repo.broken_message": "Les données git de ce dépôt ne peuvent pas être lues. Contactez l'administrateur de cette instance ou supprimez ce dépôt.",
"repo.no_branch": "Ce dépôt na aucune branche.",
@@ -1294,7 +1294,7 @@
"repo.editor.file_changed_while_editing": "Le contenu du fichier a changé depuis que vous avez commencé à éditer. <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">Cliquez ici</a> pour voir les changements ou <strong>soumettez de nouveau</strong> pour les écraser.",
"repo.editor.file_already_exists": "Un fichier nommé \"%s\" existe déjà dans ce dépôt.",
"repo.editor.commit_id_not_matching": "LID de la révision ne correspond pas à lID lorsque vous avez commencé à éditer. Faites une révision dans une branche de correctif puis fusionnez.",
"repo.editor.push_out_of_date": "Cette soumission semble être obsolète.",
"repo.editor.push_out_of_date": "Cet envoi semble être obsolète.",
"repo.editor.commit_empty_file_header": "Réviser un fichier vide",
"repo.editor.commit_empty_file_text": "Le fichier que vous allez réviser est vide. Continuer ?",
"repo.editor.no_changes_to_show": "Il ny a aucune modification à afficher.",
@@ -1307,7 +1307,7 @@
"repo.editor.upload_files_to_dir": "Téléverser les fichiers vers \"%s\"",
"repo.editor.cannot_commit_to_protected_branch": "Impossible de créer une révision sur la branche protégée \"%s\".",
"repo.editor.no_commit_to_branch": "Impossible de réviser cette branche car :",
"repo.editor.user_no_push_to_branch": "Lutilisateur ne peut pas soumettre sur la branche",
"repo.editor.user_no_push_to_branch": "L'utilisateur ne peut pas pousser vers la branche",
"repo.editor.require_signed_commit": "Cette branche nécessite une révision signée",
"repo.editor.cherry_pick": "Picorer %s vers:",
"repo.editor.revert": "Rétablir %s sur:",
@@ -1317,11 +1317,10 @@
"repo.editor.fork_create_description": "Vous ne pouvez pas modifier ce dépôt directement. Cependant, vous pouvez bifurquer ce dépôt, et créer une demande dajout avec vos contributions.",
"repo.editor.fork_edit_description": "Vous ne pouvez pas modifier ce dépôt directement. Les modifications seront écrites sur une bifurcation <b>%s</b>, vous permettant de faire une demande dajout.",
"repo.editor.fork_not_editable": "Vous avez bifurqué ce dépôt mais votre copie nest pas modifiable.",
"repo.editor.fork_failed_to_push_branch": "Impossible de soumettre la branche %s vers votre dépôt.",
"repo.editor.fork_failed_to_push_branch": "Impossible de pousser la branche %s vers votre dépôt.",
"repo.editor.fork_branch_exists": "La branche « %s » existe déjà dans votre bifurcation, veuillez choisir un nouveau nom.",
"repo.commits.desc": "Naviguer dans l'historique des modifications.",
"repo.commits.commits": "Révisions",
"repo.commits.history_enable_follow_renames": "Inclure les renommages",
"repo.commits.no_commits": "Pas de révisions en commun. \"%s\" et \"%s\" ont des historiques entièrement différents.",
"repo.commits.nothing_to_compare": "Ces révisions sont équivalentes.",
"repo.commits.search.tooltip": "Vous pouvez utiliser les mots-clés \"author:\", \"committer:\", \"after:\", ou \"before:\" pour filtrer votre recherche, ex.: \"revert author:Alice before:2019-01-13\".",
@@ -1333,8 +1332,8 @@
"repo.commits.older": "Précédemment",
"repo.commits.newer": "Récemment",
"repo.commits.signed_by": "Signé par",
"repo.commits.signed_by_untrusted_user": "Signé en dilettante par",
"repo.commits.signed_by_untrusted_user_unmatched": "Signé, sans en être lauteur, par",
"repo.commits.signed_by_untrusted_user": "Signature provenant d'un utilisateur dilletant",
"repo.commits.signed_by_untrusted_user_unmatched": "Signature discordante de l'auteur de la révision et provenant d'un utilisateur dilletant",
"repo.commits.gpg_key_id": "ID de la clé GPG",
"repo.commits.ssh_key_fingerprint": "Empreinte numérique de la clé SSH",
"repo.commits.view_path": "Voir à ce point de l'historique",
@@ -1622,7 +1621,7 @@
"repo.issues.lock.notice_2": "- Vous et les autres collaborateurs ayant accès à ce dépôt peuvent toujours laisser des commentaires que dautres peuvent voir.",
"repo.issues.lock.notice_3": "- Vous pouvez toujours déverrouiller ce ticket à l'avenir.",
"repo.issues.unlock.notice_1": "- Tout le monde sera de nouveau en mesure de commenter ce ticket.",
"repo.issues.unlock.notice_2": "- Vous pouvez toujours verrouiller ce ticket à lavenir.",
"repo.issues.unlock.notice_2": "- Vous pouvez toujours verrouiller ce ticket à l'avenir.",
"repo.issues.lock.reason": "Motif de verrouillage",
"repo.issues.lock.title": "Verrouiller la conversation sur ce ticket.",
"repo.issues.unlock.title": "Déverrouiller la conversation sur ce ticket.",
@@ -1818,9 +1817,9 @@
"repo.pulls.is_checking": "Recherche de conflits de fusion…",
"repo.pulls.is_ancestor": "Cette branche est déjà présente dans la branche ciblée. Il n'y a rien à fusionner.",
"repo.pulls.is_empty": "Les changements sur cette branche sont déjà sur la branche cible. Cette révision sera vide.",
"repo.pulls.required_status_check_failed": "Certains signaux requis n'ont pas réussi.",
"repo.pulls.required_status_check_missing": "Certains signaux requis sont manquants.",
"repo.pulls.required_status_check_administrator": "En tant quadministrateur, vous pouvez fusionner cette demande dajout.",
"repo.pulls.required_status_check_failed": "Certains contrôles requis n'ont pas réussi.",
"repo.pulls.required_status_check_missing": "Certains contrôles requis sont manquants.",
"repo.pulls.required_status_check_administrator": "En tant qu'administrateur, vous pouvez toujours fusionner cette requête de pull.",
"repo.pulls.required_status_check_bypass_allowlist": "Vous êtes autorisé à contourner les règles de protection pour cette fusion.",
"repo.pulls.blocked_by_approvals": "Cette demande dajout nest pas suffisamment approuvée. %d approbations obtenues sur %d.",
"repo.pulls.blocked_by_approvals_whitelisted": "Cette demande dajout na pas encore assez dapprobations. %d sur %d approbations de la part des utilisateurs ou équipes sur la liste autorisée.",
@@ -1844,7 +1843,7 @@
"repo.pulls.no_merge_desc": "Cette demande dajout ne peut être fusionnée car toutes les options de fusion du dépôt sont désactivées.",
"repo.pulls.no_merge_helper": "Activez des options de fusion dans les paramètres du dépôt ou fusionnez la demande manuellement.",
"repo.pulls.no_merge_wip": "Cette demande dajout ne peut pas être fusionnée car elle est marquée en chantier.",
"repo.pulls.no_merge_not_ready": "Cette demande dajout nest pas prête à être fusionnée, vérifiez les évaluations et les signaux.",
"repo.pulls.no_merge_not_ready": "Cette demande dajout nest pas prête à être fusionnée, vérifiez les évaluations et le contrôle qualité.",
"repo.pulls.no_merge_access": "Vous n'êtes pas autorisé⋅e à fusionner cette demande d'ajout.",
"repo.pulls.merge_pull_request": "Créer une révision de fusion",
"repo.pulls.rebase_merge_pull_request": "Rebaser puis rattraper",
@@ -1868,19 +1867,19 @@
"repo.pulls.push_rejected_summary": "Message de rejet complet",
"repo.pulls.push_rejected_no_message": "Échec de la fusion : la soumission a été rejetée sans raison. Contrôler les déclencheurs Git pour ce dépôt.",
"repo.pulls.open_unmerged_pull_exists": "Vous ne pouvez pas rouvrir ceci car la demande dajout #%d, en attente, a des propriétés identiques.",
"repo.pulls.status_checking": "Certains signaux sont en attente",
"repo.pulls.status_checks_success": "Tous les signaux ont réussi",
"repo.pulls.status_checks_warning": "Des signaux déclarent des avertissements",
"repo.pulls.status_checks_failure_required": "Des signaux requis ont échoués",
"repo.pulls.status_checks_failure_optional": "Des signaux optionnels ont échoués",
"repo.pulls.status_checks_error": "Des signaux rapportent des erreurs",
"repo.pulls.status_checking": "Certains contrôles sont en attente",
"repo.pulls.status_checks_success": "Tous les contrôles ont réussi",
"repo.pulls.status_checks_warning": "Quelques vérifications ont signalé des avertissements",
"repo.pulls.status_checks_failure_required": "Des vérifications obligatoires ont échoué",
"repo.pulls.status_checks_failure_optional": "Des vérifications optionnelles ont échoué",
"repo.pulls.status_checks_error": "Quelques vérifications ont signalé des erreurs",
"repo.pulls.status_checks_requested": "Requis",
"repo.pulls.status_checks_details": "Détails",
"repo.pulls.status_checks_hide_all": "Masquer les signaux",
"repo.pulls.status_checks_hide_all": "Masquer toutes les vérifications",
"repo.pulls.status_checks_show_all": "Afficher toutes les vérifications",
"repo.pulls.status_checks_approve_all": "Approuver toutes les procédures",
"repo.pulls.status_checks_need_approvals": "%d procédure(s) en attente dapprobation",
"repo.pulls.status_checks_need_approvals_helper": "Cette procédure ne sexecutera quaprès lapprobation par le mainteneur du dépôt.",
"repo.pulls.status_checks_approve_all": "Accepter tous les flux de travail",
"repo.pulls.status_checks_need_approvals": "%d flux de travail en attente dapprobation",
"repo.pulls.status_checks_need_approvals_helper": "Ce flux de travail ne sexécutera quaprès lapprobation par le mainteneur du dépôt.",
"repo.pulls.update_branch": "Actualiser la branche par fusion",
"repo.pulls.update_branch_rebase": "Actualiser la branche par rebasage",
"repo.pulls.update_branch_success": "La mise à jour de la branche a réussi",
@@ -1964,8 +1963,8 @@
"repo.ext_wiki.desc": "Lier un wiki externe.",
"repo.wiki": "Wiki",
"repo.wiki.welcome": "Bienvenue sur le Wiki.",
"repo.wiki.welcome_desc": "Le wiki vous permet de rédiger et partager de la documentation avec des collaborateurs.",
"repo.wiki.desc": "Rédiger et partager de la documentation avec des collaborateurs.",
"repo.wiki.welcome_desc": "Le wiki vous permet d'écrire ou de partager de la documentation avec vos collaborateurs.",
"repo.wiki.desc": "Écrire et partager de la documentation avec vos collaborateurs.",
"repo.wiki.create_first_page": "Créer la première page",
"repo.wiki.page": "Page",
"repo.wiki.filter_page": "Filtrer la page",
@@ -1987,7 +1986,7 @@
"repo.wiki.pages": "Pages",
"repo.wiki.last_updated": "Dernière mise à jour: %s",
"repo.wiki.page_name_desc": "Entrez un nom pour cette page Wiki. Certains noms spéciaux sont « Home », « _Sidebar » et « _Footer ».",
"repo.wiki.original_git_entry_tooltip": "Voir le fichier Git original au lieu dutiliser un lien convivial.",
"repo.wiki.original_git_entry_tooltip": "Voir le fichier Git original au lieu d'utiliser un lien convivial.",
"repo.activity": "Activité",
"repo.activity.navbar.pulse": "Impulsion",
"repo.activity.navbar.code_frequency": "Fréquence du code",
@@ -2067,23 +2066,23 @@
"repo.settings.public_access": "Accès public",
"repo.settings.public_access_desc": "Configurer les permissions des visiteurs publics remplaçant les valeurs par défaut de ce dépôt.",
"repo.settings.public_access.docs.not_set": "Non défini : ne donne aucune permission supplémentaire. Les règles du dépôt et les permissions des utilisateurs font foi.",
"repo.settings.public_access.docs.anonymous_read": "Accès anonyme : les visiteurs non connectés peuvent consulter cette section.",
"repo.settings.public_access.docs.everyone_read": "Consultation collective : tous les utilisateurs connectés peuvent consulter cette section. Pour les Tickets et les Demandes dajouts, cela permet aussi aux utilisateurs connectés den créer.",
"repo.settings.public_access.docs.everyone_write": "Participation collective : tous les utilisateurs connectés peuvent participer à la section. Seul le Wiki supporte cette permission.",
"repo.settings.public_access.docs.anonymous_read": "Lecture anonyme : les utilisateurs qui ne sont pas connectés peuvent consulter la ressource.",
"repo.settings.public_access.docs.everyone_read": "Consultation collective : tous les utilisateurs connectés peuvent consulter la ressource. Mettre les tickets et demandes dajouts en accès public signifie que les utilisateurs connectés peuvent en créer.",
"repo.settings.public_access.docs.everyone_write": "Participation collective : tous les utilisateurs connectés ont la permission décrire sur la ressource. Seule le Wiki supporte cette autorisation.",
"repo.settings.collaboration": "Collaborateurs",
"repo.settings.collaboration.admin": "Administrateur",
"repo.settings.collaboration.write": "Écriture",
"repo.settings.collaboration.read": "Lecture",
"repo.settings.collaboration.owner": "Propriétaire",
"repo.settings.collaboration.undefined": "Indéfini",
"repo.settings.collaboration.per_unit": "Permissions de section",
"repo.settings.collaboration.per_unit": "Permissions de ressource",
"repo.settings.hooks": "Déclencheurs web",
"repo.settings.githooks": "Déclencheurs Git",
"repo.settings.basic_settings": "Paramètres de base",
"repo.settings.mirror_settings": "Réglages Miroir",
"repo.settings.mirror_settings.docs": "Configurez votre dépôt pour synchroniser automatiquement les révisions, étiquettes et branches avec un autre dépôt.",
"repo.settings.mirror_settings.docs.disabled_pull_mirror.instructions": "Configurez votre projet pour soumettre automatiquement les révisions, étiquettes et branches vers un autre dépôt. Les miroirs ont été désactivés par ladministrateur de votre site.",
"repo.settings.mirror_settings.docs.disabled_push_mirror.instructions": "Configurez votre projet pour synchroniser automatiquement les révisions, étiquettes et branches dun autre dépôt.",
"repo.settings.mirror_settings.docs.disabled_pull_mirror.instructions": "Configurez votre projet pour soumettre automatiquement les révisions, étiquettes et branches vers un autre dépôt. Les miroirs ont été désactivés par l'administrateur de votre site.",
"repo.settings.mirror_settings.docs.disabled_push_mirror.instructions": "Configurez votre projet pour synchroniser automatiquement les révisions, étiquettes et branches d'un autre dépôt.",
"repo.settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning": "Pour linstant, cela ne peut être fait que dans le menu « Nouvelle migration ». Pour plus dinformations, veuillez consulter :",
"repo.settings.mirror_settings.docs.disabled_push_mirror.info": "Les miroirs push ont été désactivés par ladministrateur de votre site.",
"repo.settings.mirror_settings.docs.no_new_mirrors": "Votre dépôt se synchronise avec un dépôt distant. Vous ne pouvez pas créer de nouveaux miroirs pour le moment.",
@@ -2202,13 +2201,11 @@
"repo.settings.trust_model.default.desc": "Utiliser le niveau de confiance configuré par défaut pour cette instance Gitea.",
"repo.settings.trust_model.collaborator": "Collaborateur",
"repo.settings.trust_model.collaborator.long": "Collaborateur : ne se fier qu'aux signatures des collaborateurs du dépôt",
"repo.settings.trust_model.collaborator.desc": "Une révision est réputée authentifiée si elle est signée par un collaborateur du dépôt. Si elle nest que signée par son auteur, elle sera réputée dilettante, et discordante sinon.",
"repo.settings.trust_model.collaborator.desc": "La signature dune révision est dite « fiable » si elle correspond à un collaborateur du dépôt, indépendamment de son auteur. À défaut, si elle correspond à lauteur de la révision, elle sera « dilettante », et « discordante » sinon.",
"repo.settings.trust_model.committer": "Auteur",
"repo.settings.trust_model.committer.long": "Auteur : ne se fier quaux signatures des auteurs des révisions (mimique GitHub en forçant Gitea à co-signer ses révisions).",
"repo.settings.trust_model.committer.desc": "Une révision est réputée authentifiée si elle est signée par son auteur, et discordante si les signatures diffèrent. Cela force Gitea à signer ses propres révisions en créditant lauteur original en pied de révision \"Co-authored-by:\" et \"Co-committed-by:\". La clé par défaut de Gitea doit correspondre à celle dun utilisateur existant.",
"repo.settings.trust_model.collaboratorcommitter": "Collaborateur et Auteur",
"repo.settings.trust_model.collaboratorcommitter.long": "Collaborateur et Auteur : ne se fier qu'aux signatures des auteurs collaborant au dépôt",
"repo.settings.trust_model.collaboratorcommitter.desc": "Une révision est réputée authentifiée si est elle signée par son auteur étant lui-même collaborateur du dépôt. Si elle nest que signée par son auteur, elle sera réputée dilettante, et discordante sinon. Cela force Gitea à signer ses propres révisions en créditant lauteur original en pied de révision \"Co-authored-by:\". La clé par défaut de Gitea doit correspondre à celle dun utilisateur existant.",
"repo.settings.wiki_delete": "Supprimer les données du Wiki",
"repo.settings.wiki_delete_desc": "Supprimer les données du wiki d'un dépôt est permanent. Cette action est irréversible.",
"repo.settings.wiki_delete_notices_1": "- Ceci supprimera de manière permanente et désactivera le wiki de dépôt pour %s.",
@@ -2221,7 +2218,7 @@
"repo.settings.delete_notices_fork_1": "- Les bifurcations de ce dépôt deviendront indépendants après suppression.",
"repo.settings.deletion_success": "Le dépôt a été supprimé.",
"repo.settings.update_settings_success": "Les options du dépôt ont été mises à jour.",
"repo.settings.update_settings_no_unit": "Si vous désactivez toutes les sections du dépôt, vous ne pourrez gère plus lutiliser.",
"repo.settings.update_settings_no_unit": "Impossible de désactiver toutes les fonctionnalités d'un dépôt. Vous ne pourrez gère l'utiliser.",
"repo.settings.confirm_delete": "Supprimer le dépôt",
"repo.settings.add_collaborator": "Ajouter un collaborateur",
"repo.settings.add_collaborator_success": "Le collaborateur a été ajouté.",
@@ -2295,7 +2292,7 @@
"repo.settings.event_release": "Publication",
"repo.settings.event_release_desc": "Publication publiée, mise à jour ou supprimée.",
"repo.settings.event_push": "Soumission",
"repo.settings.event_force_push": "Soumission forcée",
"repo.settings.event_force_push": "Poussée forcée",
"repo.settings.event_push_desc": "Soumission Git.",
"repo.settings.event_repository": "Dépôt",
"repo.settings.event_repository_desc": "Dépôt créé ou supprimé.",
@@ -2329,11 +2326,11 @@
"repo.settings.event_pull_request_review_request_desc": "Création ou suppresion de demandes dévaluation.",
"repo.settings.event_pull_request_approvals": "Approbations de demande d'ajout",
"repo.settings.event_pull_request_merge": "Fusion de demande d'ajout",
"repo.settings.event_header_workflow": "Événements de procédure",
"repo.settings.event_workflow_run": "Exécution de procédure",
"repo.settings.event_workflow_run_desc": "Exécution des procédures des Actions Gitea ajoutée, en attente, en cours ou terminées.",
"repo.settings.event_workflow_job": "Missions de la procédure",
"repo.settings.event_workflow_job_desc": "Les missions ajoutées, en attente, en cours ou terminées des Actions Gitea.",
"repo.settings.event_header_workflow": "Événements du flux de travail",
"repo.settings.event_workflow_run": "Exécution du flux de travail",
"repo.settings.event_workflow_run_desc": "Tâche du flux de travail Gitea Actions ajoutée, en attente, en cours ou terminée.",
"repo.settings.event_workflow_job": "Tâches du flux de travail",
"repo.settings.event_workflow_job_desc": "Tâches du flux de travail Gitea Actions en file dattente, en attente, en cours ou terminée.",
"repo.settings.event_package": "Paquet",
"repo.settings.event_package_desc": "Paquet créé ou supprimé.",
"repo.settings.branch_filter": "Filtre de branche",
@@ -2393,44 +2390,44 @@
"repo.settings.protected_branch_can_push_no": "Vous ne pouvez pas soumettre",
"repo.settings.branch_protection": "Paramètres de protection de branches pour la branche <b>%s</b>",
"repo.settings.protect_this_branch": "Activer la protection de branche",
"repo.settings.protect_this_branch_desc": "Empêche la suppression et Git de soumettre et fusionner sur cette branche.",
"repo.settings.protect_this_branch_desc": "Empêche les suppressions et limite les poussées et fusions sur cette branche.",
"repo.settings.protect_disable_push": "Désactiver la soumission",
"repo.settings.protect_disable_push_desc": "Aucune soumission ne sera possible sur cette branche.",
"repo.settings.protect_disable_force_push": "Désactiver la soumission forcée",
"repo.settings.protect_disable_force_push_desc": "Aucune soumission forcée ne sera possible sur cette branche.",
"repo.settings.protect_disable_force_push": "Désactiver les poussés forcées",
"repo.settings.protect_disable_force_push_desc": "Aucune poussée forcée ne sera possible sur cette branche.",
"repo.settings.protect_enable_push": "Activer la soumission",
"repo.settings.protect_enable_push_desc": "Toute personne ayant un accès en écriture sera autorisée à soumettre sur cette branche (sans forcer).",
"repo.settings.protect_enable_force_push_all": "Activer la soumission forcée",
"repo.settings.protect_enable_force_push_all_desc": "Toute personne pouvant soumettre pourra forcer sur cette branche.",
"repo.settings.protect_enable_force_push_all": "Activer les poussées forcées",
"repo.settings.protect_enable_force_push_all_desc": "Toute personne pouvant pousser pourra forcer sur cette branche.",
"repo.settings.protect_enable_force_push_allowlist": "Soumission forcée sur autorisation uniquement",
"repo.settings.protect_enable_force_push_allowlist_desc": "Seuls les utilisateurs ou équipes autorisés ayants un droit de soumission seront autorisés à soumettre en force sur cette branche.",
"repo.settings.protect_enable_force_push_allowlist_desc": "Seuls les utilisateurs ou équipes autorisés ayants un droit de pousser seront autorisés à pousser en force sur cette branche.",
"repo.settings.protect_enable_merge": "Activer la fusion",
"repo.settings.protect_enable_merge_desc": "Toute personne ayant un accès en écriture sera autorisée à fusionner les demandes d'ajout dans cette branche.",
"repo.settings.protect_whitelist_committers": "Soumissions sur autorisation uniquement",
"repo.settings.protect_whitelist_committers_desc": "Seuls les utilisateurs ou les équipes autorisés pourront soumettre sur cette branche (sans forcer).",
"repo.settings.protect_whitelist_committers_desc": "Seuls les utilisateurs ou les équipes autorisés pourront pousser sur cette branche (sans forcer).",
"repo.settings.protect_whitelist_deploy_keys": "Clés de déploiement pouvant écrire autorisées à pousser.",
"repo.settings.protect_whitelist_users": "Utilisateurs autorisés à pousser :",
"repo.settings.protect_whitelist_teams": "Équipes autorisées à pousser :",
"repo.settings.protect_force_push_allowlist_users": "Utilisateurs autorisés à soumettre en force :",
"repo.settings.protect_force_push_allowlist_teams": "Équipes autorisées à soumettre en force :",
"repo.settings.protect_force_push_allowlist_deploy_keys": "Inclure aussi les clés de déploiement autorisées à soumettre.",
"repo.settings.protect_force_push_allowlist_users": "Utilisateurs autorisés à pousser en force :",
"repo.settings.protect_force_push_allowlist_teams": "Équipes autorisées à pousser en force :",
"repo.settings.protect_force_push_allowlist_deploy_keys": "Clés de déploiement pouvant pousser autorisées à pousser en force.",
"repo.settings.protect_merge_whitelist_committers": "Fusion sur autorisation uniquement",
"repo.settings.protect_merge_whitelist_committers_desc": "Nautoriser que les utilisateurs et les équipes listés à appliquer les demandes de fusion sur cette branche.",
"repo.settings.protect_merge_whitelist_users": "Utilisateurs autorisés à fusionner :",
"repo.settings.protect_merge_whitelist_teams": "Équipes autorisées à fusionner :",
"repo.settings.protect_bypass_allowlist": "Contourner la protection de la branche",
"repo.settings.protect_enable_bypass_allowlist": "Autoriser des utilisateurs et des équipes à contourner les restrictions de branche",
"repo.settings.protect_enable_bypass_allowlist_desc": "Les utilisateurs ou équipes autorisés peuvent fusionner ou soumettre des changements nonobstant les règles dapprobations, de vérifications des signaux et les protections de fichiers.",
"repo.settings.protect_enable_bypass_allowlist_desc": "Les utilisateurs ou équipes autorisés peuvent fusionner ou pousser des changements nonobstant les règles dapprobations, de vérifications de statut et les protections fichiers.",
"repo.settings.protect_bypass_allowlist_users": "Liste dutilisateurs autorisés à contourner les protections :",
"repo.settings.protect_bypass_allowlist_teams": "Liste déquipes autorisées à contourner les protections :",
"repo.settings.protect_check_status_contexts": "Activer les signaux",
"repo.settings.protect_status_check_patterns": "Motifs de signal :",
"repo.settings.protect_status_check_patterns_desc": "Entrez des motifs pour spécifier quelles signaux doivent réussir avant que des branches puissent être fusionnées. Un motif par ligne. Un motif ne peut être vide.",
"repo.settings.protect_check_status_contexts_desc": "Exiger la réussite des signaux avant de fusionner. Quand activé, une branche protégée ne peux accepter que des soumissions ou des fusions ayant le status « succès ». Lorsqu'il ny a pas de contexte, la dernière révision fait foi.",
"repo.settings.protect_check_status_contexts_list": "Signaux trouvés au cours de la semaine passée pour ce dépôt",
"repo.settings.protect_check_status_contexts": "Activer le Contrôle Qualité",
"repo.settings.protect_status_check_patterns": "Motifs de vérification des statuts :",
"repo.settings.protect_status_check_patterns_desc": "Entrez des motifs pour spécifier quelles vérifications doivent réussir avant que des branches puissent être fusionnées. Un motif par ligne. Un motif ne peut être vide.",
"repo.settings.protect_check_status_contexts_desc": "Exiger le status « succès » avant de fusionner. Quand activée, une branche protégée ne peux accepter que des soumissions ou des fusions ayant le status « succès ». Lorsqu'il ny a pas de contexte, la dernière révision fait foi.",
"repo.settings.protect_check_status_contexts_list": "Contrôles qualité trouvés au cours de la semaine dernière pour ce dépôt",
"repo.settings.protect_status_check_matched": "Correspondant",
"repo.settings.protect_invalid_status_check_pattern": "Motif de signal invalide : « %s ».",
"repo.settings.protect_no_valid_status_check_patterns": "Aucun motif de signaux valide.",
"repo.settings.protect_invalid_status_check_pattern": "Motif de vérification des statuts incorrect : « %s ».",
"repo.settings.protect_no_valid_status_check_patterns": "Aucun motif de vérification des statuts valide.",
"repo.settings.protect_required_approvals": "Minimum d'approbations requis :",
"repo.settings.protect_required_approvals_desc": "Permet de fusionner les demandes dajout lorsque suffisamment dévaluation sont positives.",
"repo.settings.protect_approvals_whitelist_enabled": "Restreindre les approbations sur autorisation uniquement",
@@ -2456,15 +2453,15 @@
"repo.settings.remove_protected_branch_success": "La règle de protection de branche \"%s\" a été retirée.",
"repo.settings.remove_protected_branch_failed": "Impossible de retirer la règle de protection de branche \"%s\".",
"repo.settings.protected_branch_deletion": "Désactiver la protection de branche",
"repo.settings.protected_branch_deletion_desc": "Désactiver la protection de branche permet aux utilisateurs ayant accès en écriture de soumettre des modifications sur la branche. Continuer ?",
"repo.settings.protected_branch_deletion_desc": "Désactiver la protection de branche permet aux utilisateurs ayant accès en écriture de pousser des modifications sur la branche. Continuer ?",
"repo.settings.block_rejected_reviews": "Bloquer la fusion en cas dévaluations négatives",
"repo.settings.block_rejected_reviews_desc": "La fusion ne sera pas possible lorsque des modifications sont demandées par les évaluateurs officiels, même s'il y a suffisamment dapprobations.",
"repo.settings.block_on_official_review_requests": "Bloquer la fusion en cas de demande dévaluation officielle",
"repo.settings.block_on_official_review_requests_desc": "La fusion ne sera pas possible tant quelle aura des demandes dévaluations officielles, même s'il y a suffisamment dapprobations.",
"repo.settings.block_outdated_branch": "Bloquer la fusion si la demande d'ajout est obsolète",
"repo.settings.block_outdated_branch_desc": "La fusion ne sera pas possible lorsque la branche principale est derrière la branche de base.",
"repo.settings.block_admin_merge_override": "Inclure les administrateurs dans lapplication de la règle",
"repo.settings.block_admin_merge_override_desc": "Les administrateurs sont également soumis aux règles de protection des branches. En revanche, les utilisateurs et équipes qui figurent sur la liste dérogatoire y sont exemptés.",
"repo.settings.block_admin_merge_override": "Les administrateurs doivent respecter les règles de protection des branches",
"repo.settings.block_admin_merge_override_desc": "Les administrateurs sont également soumis aux règles de protection des branches. En activant la liste dautorisation de contournement, les utilisateurs et équipes qui y figurent peuvent toujours contourner ces règles.",
"repo.settings.default_branch_desc": "Sélectionnez une branche par défaut pour les révisions.",
"repo.settings.default_target_branch_desc": "Les demandes dajout peuvent utiliser une branche cible différente, telle que définie dans la section Demandes dajouts des Paramètres avancés du dépôt.",
"repo.settings.merge_style_desc": "Styles de fusion",
@@ -2599,9 +2596,7 @@
"repo.diff.review.reject": "Demander des changements",
"repo.diff.review.self_approve": "Les auteurs dune demande dajout ne peuvent pas approuver leur propre demande dajout",
"repo.diff.committed_by": "révisé par",
"repo.diff.coauthored_by": "coécrit par",
"repo.commits.avatar_stack_and": "et",
"repo.commits.avatar_stack_people": "%d personne(s)",
"repo.diff.protected": "Protégé",
"repo.diff.image.side_by_side": "Côte à côte",
"repo.diff.image.swipe": "Glisser",
@@ -2721,7 +2716,7 @@
"repo.error.csv.too_large": "Impossible de visualiser le fichier car il est trop volumineux.",
"repo.error.csv.unexpected": "Impossible de visualiser ce fichier car il contient un caractère inattendu ligne %d, colonne %d.",
"repo.error.csv.invalid_field_count": "Impossible de visualiser ce fichier car il contient un nombre de champs incorrect à la ligne %d.",
"repo.error.broken_git_hook": "Les crochets Git de ce dépôt semblent cassés. Veuillez suivre la <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a> pour les corriger, puis soummettre des révisions pour actualiser le statut.",
"repo.error.broken_git_hook": "Les crochets Git de ce dépôt semblent cassés. Veuillez suivre la <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a> pour les corriger, puis pousser des révisions pour actualiser le statut.",
"graphs.component_loading": "Chargement de %s…",
"graphs.component_loading_failed": "Impossible de charger %s.",
"graphs.component_loading_info": "Ça prend son temps…",
@@ -2729,7 +2724,6 @@
"graphs.code_frequency.what": "fréquence du code",
"graphs.contributors.what": "contributions",
"graphs.recent_commits.what": "révisions récentes",
"graphs.chart_zoom_hint": "Glisser : zoom, Maj + Glisser : pano, Double-clic : recentrer",
"org.org_name_holder": "Nom de l'organisation",
"org.org_full_name_holder": "Nom complet de l'organisation",
"org.org_name_helper": "Le nom de l'organisation doit être court et mémorable.",
@@ -2751,8 +2745,8 @@
"org.team_desc_helper": "Décrire le but ou le rôle de léquipe.",
"org.team_access_desc": "Accès au dépôt",
"org.team_permission_desc": "Autorisation",
"org.team_unit_desc": "Permettre laccès aux sections du dépôt",
"org.team_unit_disabled": "(Désactivée)",
"org.team_unit_desc": "Permettre laccès aux Sections du dépôt",
"org.team_unit_disabled": "(Désactivé)",
"org.form.name_been_taken": "Le nom dorganisation « %s » a déjà été utilisé.",
"org.form.name_reserved": "Le nom d'organisation \"%s\" est réservé.",
"org.form.name_pattern_not_allowed": "Le motif « %s » n'est pas autorisé dans un nom d'organisation.",
@@ -2820,15 +2814,15 @@
"org.teams.can_create_org_repo": "Créer des dépôts",
"org.teams.can_create_org_repo_helper": "Les membres peuvent créer de nouveaux dépôts dans l'organisation. Le créateur obtiendra l'accès administrateur au nouveau dépôt.",
"org.teams.none_access": "Aucun accès",
"org.teams.none_access_helper": "Les membres ne peuvent ni consulter ni participer à cette section. Ne sapplique pas pour les dépôts publics.",
"org.teams.none_access_helper": "Les membres ne peuvent voir ou faire quoi que ce soit sur cette partie. Sans effet pour les dépôts publics.",
"org.teams.general_access": "Accès général",
"org.teams.general_access_helper": "Les permissions des membres seront déterminées par la table des permissions ci-dessous.",
"org.teams.read_access": "Lecture",
"org.teams.read_access_helper": "Les membres peuvent voir et cloner les dépôts de léquipe.",
"org.teams.read_access_helper": "Les membres peuvent voir et cloner les dépôts de l'équipe.",
"org.teams.write_access": "Écriture",
"org.teams.write_access_helper": "Les membres peuvent consulter et soumettre sur les dépôts de léquipe.",
"org.teams.write_access_helper": "Les membres peuvent voir et pousser dans les dépôts de l'équipe.",
"org.teams.admin_access": "Accès Administrateur",
"org.teams.admin_access_helper": "Les membres peuvent extraire et soumettre les dépôts de léquipe et y ajouter des collaborateurs.",
"org.teams.admin_access_helper": "Les membres peuvent tirer et pousser des modifications vers les dépôts de l'équipe, et y ajouter des collaborateurs.",
"org.teams.no_desc": "Aucune description",
"org.teams.settings": "Paramètres",
"org.teams.owners_permission_desc": "Les propriétaires ont un accès complet à <strong>tous les dépôts</strong> et disposent <strong> d'un accès administrateur</strong> de l'organisation.",
@@ -2845,8 +2839,8 @@
"org.teams.delete_team_desc": "Supprimer une équipe supprime l'accès aux dépôts à ses membres. Continuer ?",
"org.teams.delete_team_success": "Léquipe a été supprimée.",
"org.teams.read_permission_desc": "Cette équipe permet l'accès en <strong>lecture</strong> : les membres peuvent voir et dupliquer ses dépôts.",
"org.teams.write_permission_desc": "Cette équipe accorde un accès en <strong>écriture</strong> : les membres peuvent participer à ses dépôts.",
"org.teams.admin_permission_desc": "Cette équipe accorde un accès <strong>administrateur</strong> : les membres peuvent consulter, participer et ajouter des collaborateurs à ses dépôts.",
"org.teams.write_permission_desc": "Cette équipe permet l'accès en <strong>écriture</strong> : les membres peuvent participer à ses dépôts.",
"org.teams.admin_permission_desc": "Cette équipe permet l'accès <strong>administrateur</strong> : les membres peuvent voir, participer et ajouter des collaborateurs à ses dépôts.",
"org.teams.create_repo_permission_desc": "De plus, cette équipe accorde la permission <strong>Créer un dépôt</strong> : les membres peuvent créer de nouveaux dépôts dans l'organisation.",
"org.teams.repositories": "Dépôts de l'Équipe",
"org.teams.remove_all_repos_title": "Supprimer tous les dépôts de l'équipe",
@@ -2863,8 +2857,8 @@
"org.teams.all_repositories": "Tous les dépôts",
"org.teams.all_repositories_helper": "L'équipe a accès à tous les dépôts. Sélectionner ceci <strong>ajoutera tous les dépôts existants</strong> à l'équipe.",
"org.teams.all_repositories_read_permission_desc": "Cette équipe accorde l'accès <strong>en lecture</strong> à <strong>tous les dépôts</strong> : les membres peuvent voir et cloner les dépôts.",
"org.teams.all_repositories_write_permission_desc": "Cette équipe accorde un accès <strong>en écriture</strong> à <strong>tous les dépôts</strong> : les membres peuvent participer aux dépôts.",
"org.teams.all_repositories_admin_permission_desc": "Cette équipe accorde un accès <strong>administrateur</strong> à <strong>tous les dépôts</strong> : les membres peuvent consulter, participer et ajouter des collaborateurs aux dépôts.",
"org.teams.all_repositories_write_permission_desc": "Cette équipe accorde l'accès <strong>en écriture</strong> à <strong>tous les dépôts</strong> : les membres peuvent lire et écrire dans les dépôts.",
"org.teams.all_repositories_admin_permission_desc": "Cette équipe accorde l'accès <strong>administrateur</strong> à <strong>tous les dépôts</strong> : les membres peuvent lire, écrire dans et ajouter des collaborateurs aux dépôts.",
"org.teams.visibility": "Visibilité",
"org.teams.visibility_private": "Privé",
"org.teams.visibility_private_helper": "Visible uniquement aux membres de léquipe et aux propriétaires de lorganisation.",
@@ -3014,7 +3008,7 @@
"admin.dashboard.gc_lfs": "Purger les métaobjets LFS",
"admin.dashboard.stop_zombie_tasks": "Arrêter les tâches zombies",
"admin.dashboard.stop_endless_tasks": "Arrêter les tâches interminables",
"admin.dashboard.cancel_abandoned_jobs": "Annuler les actions des missions abandonnées",
"admin.dashboard.cancel_abandoned_jobs": "Annuler les actions des tâches abandonnés",
"admin.dashboard.start_schedule_tasks": "Démarrer les tâches planifiées",
"admin.dashboard.sync_branch.started": "Début de la synchronisation des branches",
"admin.dashboard.sync_tag.started": "Synchronisation des étiquettes",
@@ -3451,7 +3445,7 @@
"action.merge_pull_request": "a fusionné la demande dajout <a href=\"%[1]s\">%[3]s#%[2]s</a>",
"action.auto_merge_pull_request": "a fusionné automatiquement la demande dajout <a href=\"%[1]s\">%[3]s#%[2]s</a>",
"action.transfer_repo": "a transféré le dépôt <code>%s</code> vers <a href=\"%s\">%s</a>",
"action.push_tag": "a soumis létiquette <a href=\"%[2]s\">%[3]s</a> de <a href=\"%[1]s\">%[4]s</a>",
"action.push_tag": "a poussé létiquette <a href=\"%[2]s\">%[3]s</a> de <a href=\"%[1]s\">%[4]s</a>",
"action.delete_tag": "a supprimé létiquette %[2]s de <a href=\"%[1]s\">%[3]s</a>",
"action.delete_branch": "a supprimé la branche %[2]s de <a href=\"%[1]s\">%[3]s</a>",
"action.compare_branch": "Comparer",
@@ -3511,9 +3505,9 @@
"gpg.error.failed_retrieval_gpg_keys": "Impossible de récupérer la clé liée au compte de l'auteur",
"gpg.error.probable_bad_signature": "AVERTISSEMENT ! Bien quil y ait une clé avec cet ID dans la base de données, elle ne vérifie pas cette révision ! Cette révision est SUSPECTE.",
"gpg.error.probable_bad_default_signature": "AVERTISSEMENT ! Bien que la clé par défaut ait cet ID, elle ne vérifie pas cette révision ! Cette révision est SUSPECTE.",
"units.unit": "Section",
"units.error.no_unit_allowed_repo": "Vous nêtes pas autorisé à accéder à quelconque section de ce dépôt.",
"units.error.unit_not_allowed": "Vous nêtes pas autorisé à accéder à cette section du dépôt.",
"units.unit": "Ressource",
"units.error.no_unit_allowed_repo": "Vous n'êtes pas autorisé à accéder à n'importe quelle section de ce dépôt.",
"units.error.unit_not_allowed": "Vous n'êtes pas autorisé à accéder à cette section du dépôt.",
"packages.title": "Paquets",
"packages.desc": "Gérer les paquets du dépôt.",
"packages.empty": "Il n'y pas de paquet pour le moment.",
@@ -3727,66 +3721,64 @@
"actions.status.cancelling": "Annulation",
"actions.status.skipped": "Ignoré",
"actions.status.blocked": "Bloqué",
"actions.runners": "Opérateurs",
"actions.runners.runner_manage_panel": "Gestion des opérateurs",
"actions.runners.new": "Créer un nouvel opérateur",
"actions.runners.new_notice": "Comment démarrer un opérateur",
"actions.runners": "Exécuteurs",
"actions.runners.runner_manage_panel": "Gestion des exécuteurs",
"actions.runners.new": "Créer un nouvel exécuteur",
"actions.runners.new_notice": "Comment démarrer un exécuteur",
"actions.runners.status": "Statut",
"actions.runners.id": "ID",
"actions.runners.name": "Nom",
"actions.runners.owner_type": "Type",
"actions.runners.availability": "Activation",
"actions.runners.availability": "Disponibilité",
"actions.runners.description": "Description",
"actions.runners.labels": "Libels",
"actions.runners.labels": "Labels",
"actions.runners.last_online": "Dernière fois en ligne",
"actions.runners.runner_title": "Opérateur",
"actions.runners.task_list": "Tâches récentes de cet opérateur",
"actions.runners.runner_title": "Exécuteur",
"actions.runners.task_list": "Tâches récentes sur cet exécuteur",
"actions.runners.task_list.no_tasks": "Il n'y a pas de tâche ici.",
"actions.runners.task_list.run": "Exécution",
"actions.runners.task_list.run": "Exécuter",
"actions.runners.task_list.status": "Statut",
"actions.runners.task_list.repository": "Dépôt",
"actions.runners.task_list.commit": "Révision",
"actions.runners.task_list.done_at": "Fait à",
"actions.runners.edit_runner": "Éditer lopérateur",
"actions.runners.edit_runner": "Éditer l'Exécuteur",
"actions.runners.update_runner": "Appliquer les modifications",
"actions.runners.update_runner_success": "Opérateur mis à jour avec succès",
"actions.runners.update_runner_failed": "Impossible d'actualiser lopérateur",
"actions.runners.enable_runner": "Activer cet opérateur",
"actions.runners.enable_runner_success": "Opérateur activé avec succès",
"actions.runners.enable_runner_failed": "Impossible dactiver lopérateur",
"actions.runners.disable_runner": "Désactiver cet opérateur",
"actions.runners.disable_runner_success": "Opérateur désactivé avec succès",
"actions.runners.disable_runner_failed": "Impossible de désactiver lopérateur",
"actions.runners.delete_runner": "Supprimer cet opérateur",
"actions.runners.delete_runner_success": "Opérateur supprimé avec succès",
"actions.runners.delete_runner_failed": "Impossible de supprimer lopérateur",
"actions.runners.delete_runner_header": "Êtes-vous sûr de vouloir supprimer cet opérateur ?",
"actions.runners.delete_runner_notice": "Si des tâches sont en cours de réalisation par cet opérateur, elles seront abandonnées et marquées en échec, pouvant également entrainer léchec des procédures appelantes.",
"actions.runners.none": "Aucun opérateur disponible",
"actions.runners.update_runner_success": "Exécuteur mis à jour avec succès",
"actions.runners.update_runner_failed": "Impossible d'actualiser l'Exécuteur",
"actions.runners.enable_runner": "Activer cet exécuteur",
"actions.runners.enable_runner_success": "Exécuteur activé avec succès",
"actions.runners.enable_runner_failed": "Impossible dactiver lexécuteur",
"actions.runners.disable_runner": "Désactiver cet exécuteur",
"actions.runners.disable_runner_success": "Exécuteur désactivé avec succès",
"actions.runners.disable_runner_failed": "Impossible de désactiver lexécuteur",
"actions.runners.delete_runner": "Supprimer cet exécuteur",
"actions.runners.delete_runner_success": "Exécuteur supprimé avec succès",
"actions.runners.delete_runner_failed": "Impossible de supprimer l'Exécuteur",
"actions.runners.delete_runner_header": "Êtes-vous sûr de vouloir supprimer cet exécuteur ?",
"actions.runners.delete_runner_notice": "Si une tâche est en cours sur cet exécuteur, elle sera terminée et marquée comme échouée. Cela risque dinterrompre le flux de travail.",
"actions.runners.none": "Aucun exécuteur disponible",
"actions.runners.status.unspecified": "Inconnu",
"actions.runners.status.idle": "Disponible",
"actions.runners.status.active": "Occupé",
"actions.runners.status.idle": "Inactif",
"actions.runners.status.active": "Actif",
"actions.runners.status.offline": "Hors-ligne",
"actions.runners.version": "Version",
"actions.runners.reset_registration_token": "Réinitialiser le jeton denregistrement",
"actions.runners.reset_registration_token": "Réinitialiser le jeton d'enregistrement",
"actions.runners.reset_registration_token_confirm": "Voulez-vous révoquer le jeton actuel et en générer un nouveau ?",
"actions.runners.reset_registration_token_success": "Le jeton dinscription de lopérateur a été réinitialisé avec succès",
"actions.runs.all_workflows": "Toutes les procédures",
"actions.runs.other_workflows": "Autres procédures",
"actions.runs.other_workflows_tooltip": "Les procédures qui ont été exécutées dans ce dépôt mais qui nexistent pas dans la branche par défaut.",
"actions.runs.workflow_run_count_1": "%d exécution de procédure",
"actions.runs.workflow_run_count_n": "%d exécutions de procédure",
"actions.runners.reset_registration_token_success": "Le jeton dinscription de lexécuteur a été réinitialisé avec succès",
"actions.runs.all_workflows": "Tous les flux de travail",
"actions.runs.other_workflows": "Autres flux de travail",
"actions.runs.other_workflows_tooltip": "Les flux de travail qui ont été exécutés dans ce dépôt mais qui nexistent pas dans la branche par défaut.",
"actions.runs.workflow_run_count_1": "%d exécution du workflow",
"actions.runs.workflow_run_count_n": "%d exécutions du workflow",
"actions.runs.commit": "Révision",
"actions.runs.run_details": "Détails de lexécution",
"actions.runs.workflow_file": "Déclaration de la procédure",
"actions.runs.workflow_file_no_permission": "Pas de permission pour voir la procédure",
"actions.runs.workflow_file": "Fichier de flux de travail",
"actions.runs.scheduled": "Planifié",
"actions.runs.pushed_by": "soumis par",
"actions.runs.invalid_workflow_helper": "La déclaration de la procédure est invalide. Veuillez vérifier le fichier « %s ».",
"actions.runs.no_matching_online_runner_helper": "Aucun opérateur disponible correspondant au libellé « %s »",
"actions.runs.no_job_without_needs": "La procédure doit contenir au moins une mission sans dépendance.",
"actions.runs.no_job": "La procédure doit contenir au moins une mission.",
"actions.runs.invalid_reusable_workflow_uses": "Clause \"uses\" invalide dans la procédure : %s",
"actions.runs.invalid_workflow_helper": "La configuration du flux de travail est invalide. Veuillez vérifier votre fichier %s.",
"actions.runs.no_matching_online_runner_helper": "Aucun exécuteur en ligne correspondant au libellé %s",
"actions.runs.no_job_without_needs": "Le flux de travail doit contenir au moins une tâche sans dépendance.",
"actions.runs.no_job": "Le flux de travail doit contenir au moins une tâche",
"actions.runs.actor": "Acteur",
"actions.runs.status": "Statut",
"actions.runs.actors_no_select": "Tous les acteurs",
@@ -3794,82 +3786,45 @@
"actions.runs.branch": "Branche",
"actions.runs.branches_no_select": "Toutes les branches",
"actions.runs.no_results": "Aucun résultat correspondant.",
"actions.runs.no_workflows": "Il ny a pas de procédure ici.",
"actions.runs.no_workflows.quick_start": "Vous découvrez les Actions Gitea ? Consultez <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">le didacticiel</a>.",
"actions.runs.no_workflows.documentation": "Pour plus dinformations sur les Actions Gitea, voir <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">la documentation</a>.",
"actions.runs.no_runs": "Cette procédure na pas encore été exécutée.",
"actions.runs.no_workflows": "Il n'y a pas encore de workflows.",
"actions.runs.no_workflows.quick_start": "Vous découvrez les Actions Gitea ? Consultez <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">le didacticiel</a>.",
"actions.runs.no_workflows.documentation": "Pour plus dinformations sur les actions Gitea, voir <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">la documentation</a>.",
"actions.runs.no_runs": "Le flux de travail n'a pas encore d'exécution.",
"actions.runs.empty_commit_message": "(message de révision vide)",
"actions.runs.expire_log_message": "Les journaux ont été supprimés car ils étaient trop anciens.",
"actions.runs.delete": "Effacer lexecution de cette procédure",
"actions.runs.cancel": "Annuler lexécution de cette procédure",
"actions.runs.delete": "Supprimer cette exécution",
"actions.runs.cancel": "Annuler lexécution du flux",
"actions.runs.delete.description": "Êtes-vous sûr de vouloir supprimer définitivement cette exécution ? Cette action ne peut pas être annulée.",
"actions.runs.not_done": "Cette procédure nest pas terminée.",
"actions.runs.view_workflow_file": "Voir la déclaration de la procédure",
"actions.runs.not_done": "Cette exécution du flux de travail nest pas terminée.",
"actions.runs.view_workflow_file": "Voir le fichier du flux de travail",
"actions.runs.summary": "Résumé",
"actions.runs.all_jobs": "Toutes les missions",
"actions.runs.job_summaries": "Résumé des missions",
"actions.runs.expand_caller_jobs": "Afficher les missions de cette procédure réutilisable",
"actions.runs.collapse_caller_jobs": "Masquer les missions de cette procédure réutilisable",
"actions.runs.all_jobs": "Toutes les tâches",
"actions.runs.attempt": "Tentative",
"actions.runs.latest": "Dernière",
"actions.runs.latest_attempt": "Dernière tentative",
"actions.runs.triggered_via": "Déclenché via %s",
"actions.runs.rerun_triggered": "Relance enclenchée",
"actions.runs.back_to_pull_request": "Retour à la demande dajout",
"actions.runs.back_to_workflow": "Retour à la procédure",
"actions.runs.total_duration": "Durée totale :",
"actions.runs.workflow_dependencies": "Dépendances de la procédure",
"actions.runs.graph_jobs_count_1": "%d mission",
"actions.runs.graph_jobs_count_n": "%d missions",
"actions.runs.graph_dependencies_count_1": "%d dépendance",
"actions.runs.graph_dependencies_count_n": "%d dépendances",
"actions.runs.graph_success_rate": "%s succès",
"actions.runs.graph_zoom_in": "Zoomer (Ctrl/⌘ + défilement)",
"actions.runs.graph_zoom_max": "Déjà à 100%",
"actions.runs.graph_zoom_max": "Déjà zoomé à 100%",
"actions.runs.graph_zoom_out": "Dézoomer (Ctrl/⌘ + défilement)",
"actions.runs.graph_reset_view": "Rétablir",
"actions.workflow.disable": "Désactiver la procédure",
"actions.workflow.disable_success": "La procédure « %s » a bien été désactivée.",
"actions.workflow.enable": "Activer la procédure",
"actions.workflow.enable_success": "La procédure « %s » a bien été activée.",
"actions.workflow.disabled": "La procédure est désactivée.",
"actions.workflow.disable": "Désactiver le flux de travail",
"actions.workflow.disable_success": "Le flux de travail « %s » a bien été désactivé.",
"actions.workflow.enable": "Activer le flux de travail",
"actions.workflow.enable_success": "Le flux de travail « %s » a bien été activé.",
"actions.workflow.disabled": "Le flux de travail est désactivé.",
"actions.workflow.scope_owner": "Propriétaire",
"actions.workflow.scope_global": "Global",
"actions.workflow.required": "Requis",
"actions.workflow.scoped_required_cannot_disable": "Cette procédure transversale est requise.",
"actions.scoped_workflows": "Procédures transversales",
"actions.scoped_workflows.desc_org": "Enrôlez un dépôt afin de rendre ses procédures accessibles à votre organisation. Toutes les procédures de la branche principale de ce dépôt seront ainsi exécutées dans chaque dépôt de cette organisation, comme si elles y avaient été créées.",
"actions.scoped_workflows.desc_user": "Enrôlez un dépôt afin de rendre ses procédures accessibles à votre compte. Toutes les procédures de la branche principale de ce dépôt seront ainsi exécutées dans chaque dépôt que vous possédez, comme si elles y avaient été créées.",
"actions.scoped_workflows.desc_global": "Enrôlez un dépôt afin de rendre ses procédures accessibles à lensemble du serveur. Toutes les procédures de la branche principale de ce dépôt seront ainsi exécutées dans chaque dépôt, comme si elles y avaient été créées. Sur un serveur volumineux, ces procédures peuvent lourdement solliciter les ressources du système.",
"actions.scoped_workflows.add_help": "Pour rendre des procédures transversales, soumettez leurs déclarations dans le dossier <code>%s</code> sur la branche par défaut de ce dépôt, puis enrôlez celui-ci ci-dessous.",
"actions.scoped_workflows.security_note": "Parce quune procédure transversale opère sur dautres dépôts que le sien, ses extrants sont journalisés sur ces dépôts et sont donc consultable par leurs utilisateurs. Ainsi, une procédure issue dun dépôt privé peut-être reconstruit à partir des journaux qu'elle produit. Exposer une procédure transversale peut donc compromètre la confidentialité de son dépôt hôte. Si une procédure transversale référence une autre procédure issue dun dépôt privé, assurez-vous que les dépôts affectés puissent également s'y référer, sans quoi ces procédures échoueront.",
"actions.scoped_workflows.source.add": "Enrôler un dépôt",
"actions.scoped_workflows.source.add_success": "Dépôt enrôlé.",
"actions.scoped_workflows.source.remove_success": "Dépôt retiré.",
"actions.scoped_workflows.source.not_found": "Dépôt introuvable.",
"actions.scoped_workflows.required.update_success": "Procédure requise mise à jour.",
"actions.scoped_workflows.required.label": "Marquer les procédures comme requises (elles ne pourront être désactivés depuis un dépôt) :",
"actions.scoped_workflows.required.patterns": "Motifs de signal requis",
"actions.scoped_workflows.required.patterns_aria": "Motifs de signal requis pour « %s »",
"actions.scoped_workflows.required.patterns_note": "est uniquement appliqué lorsque la procédure est requise",
"actions.scoped_workflows.required.patterns_hint": "Marquez la procédure comme requise pour configurer ses motifs de signal.",
"actions.scoped_workflows.required.patterns_help": "Un motif de signal par ligne. Une demande dajout concernée peut être fusionnée à condition quau moins un signal par motif ait réussi. Cela est imposé pour toutes les branches affectées ayant des règles de protections (même désactivées), mais pas les branches sans protections.",
"actions.scoped_workflows.required.patterns_empty": "Chaque procédure requise nécessite au moins un motif de signal.",
"actions.scoped_workflows.required.missing_file": "Le fichier nest plus dans le dépôt enrôlé.",
"actions.scoped_workflows.required.expected_contexts": "Signaux attendus (un signal qui correspond au motif est marqué)",
"actions.scoped_workflows.required.no_status_contexts": "Comme cette procédure ne publie aucun signal, la rentre obligatoire empêchera toutes demandes dajouts concernées dêtre fusionnées. Préférez laisser cette procédure facultative.",
"actions.scoped_workflows.no_files": "Aucune procédure transversale n'a été trouvé dans la branche par défaut.",
"actions.workflow.run": "Réaliser la procédure",
"actions.workflow.create_status_badge": "Créer un badge détat",
"actions.workflow.status_badge": "Badge détat",
"actions.workflow.status_badge_url": "URL du badge",
"actions.workflow.not_found": "La procédure « %s » est introuvable.",
"actions.workflow.run_success": "La procédure « %s » est accomplie.",
"actions.workflow.from_ref": "Utiliser la procédure depuis",
"actions.workflow.has_workflow_dispatch": "Cette procédure dispose dun déclencheur workflow_dispatch.",
"actions.workflow.has_no_workflow_dispatch": "La procédure « %s » na pas de déclencheur workflow_dispatch.",
"actions.need_approval_desc": "Une approbation est nécessaire pour exécuter les procédures dune demande dajout de bifurcation.",
"actions.approve_all_success": "Toutes les procédures ont été approuvées.",
"actions.workflow.run": "Exécuter le flux de travail",
"actions.workflow.not_found": "Flux de travail « %s » introuvable.",
"actions.workflow.run_success": "Le flux de travail « %s » sest correctement exécuté.",
"actions.workflow.from_ref": "Utiliser le flux de travail depuis",
"actions.workflow.has_workflow_dispatch": "Ce flux de travail a un déclencheur dévénement workflow_dispatch.",
"actions.workflow.has_no_workflow_dispatch": "Le flux de travail %s na pas de déclencheur dévénement workflow_dispatch.",
"actions.need_approval_desc": "Besoin dapprobation pour exécuter des flux de travail pour une demande dajout de bifurcation.",
"actions.approve_all_success": "Tous les flux de travail ont été acceptés.",
"actions.variables": "Variables",
"actions.variables.management": "Gestion des variables",
"actions.variables.creation": "Ajouter une variable",
@@ -3890,7 +3845,7 @@
"actions.general": "Général",
"actions.general.enable_actions": "Activer les actions",
"actions.general.collaborative_owners_management": "Gestion des collaborateurs",
"actions.general.collaborative_owners_management_help": "Un collaborateur est un utilisateur ou une organisation dont le dépôt privé peut accéder aux actions et procédures de ce dépôt.",
"actions.general.collaborative_owners_management_help": "Un collaborateur est un utilisateur ou une organisation dont le dépôt privé peut accéder aux actions et flux de travail de ce dépôt.",
"actions.general.add_collaborative_owner": "Ajouter un collaborateur",
"actions.general.collaborative_owner_not_exist": "Le collaborateur nexiste pas.",
"actions.general.remove_collaborative_owner": "Supprimer le collaborateur",
@@ -3908,21 +3863,21 @@
"git.filemode.symbolic_link": "Lien symbolique",
"git.filemode.submodule": "Sous-module",
"org.repos.none": "Aucun dépôt.",
"actions.general.permissions": "Permissions intégrées au jeton dActions",
"actions.general.token_permissions.mode": "Régime de restriction par défaut",
"actions.general.token_permissions.mode.desc": "Lorsquune procédure ne contient pas de restriction, elle est alors restreinte au régime par défaut du dépôt.",
"actions.general.token_permissions.mode.permissive": "Régime permissif",
"actions.general.token_permissions.mode.permissive.desc": "Les missions peuvent consulter et modifier ce dépôt.",
"actions.general.token_permissions.mode.restricted": "Régime limitant",
"actions.general.token_permissions.mode.restricted.desc": "Les missions ne peuvent que consulter le contenu (code et publications) de ce dépôt.",
"actions.general.token_permissions.override_owner": "Outrepasser la configuration du propriétaire",
"actions.general.token_permissions.override_owner_desc": "Si actif, ce dépôt donnera ses propres restrictions à la place de celles du propriétaire (utilisateur ou organisation).",
"actions.general.token_permissions.maximum": "Restrictions affinées",
"actions.general.token_permissions.maximum.description": "Pour protéger plus finement le dépôt, les restrictions appliquées aux missions peuvent être définies pour chaque section individuellement.",
"actions.general.token_permissions.fork_pr_note": "Lorsquune mission est initiée par une demande dajout depuis une bifurcation, ses restrictions effectives ne peuvent passer la lecture seule.",
"actions.general.token_permissions.customize_max_permissions": "Affiner les restrictions",
"actions.general.permissions": "Permissions du jeton des actions",
"actions.general.token_permissions.mode": "Permissions par défaut du jeton",
"actions.general.token_permissions.mode.desc": "Une tâche dActions utilisera les permissions par défaut si aucune nest déclarée dans le fichier du flux de travail.",
"actions.general.token_permissions.mode.permissive": "Permissif",
"actions.general.token_permissions.mode.permissive.desc": "Permissions en lecture et écriture sur le dépôt de la tâche.",
"actions.general.token_permissions.mode.restricted": "Restreint",
"actions.general.token_permissions.mode.restricted.desc": "Permissions en lecture seule pour le contenu (code, publications) sur le dépôt de la tâche.",
"actions.general.token_permissions.override_owner": "Écraser la configuration faite par le propriétaire",
"actions.general.token_permissions.override_owner_desc": "Si actif, ce dépôt utilisera sa propre configuration pour les actions au lieu de respecter celle du propriétaire (utilisateur ou organisation).",
"actions.general.token_permissions.maximum": "Permissions maximales du jeton",
"actions.general.token_permissions.maximum.description": "Les permissions effectives de la tâche des actions seront limitées par les permissions maximales.",
"actions.general.token_permissions.fork_pr_note": "Si une tâche est démarrée par une demande de fusion depuis une bifurcation, ses permissions effectives ne dépasseront pas les permissions en lecture-seule.",
"actions.general.token_permissions.customize_max_permissions": "Personnaliser les permissions maximales",
"actions.general.cross_repo": "Accès inter-dépôt",
"actions.general.cross_repo_desc": "Permet aux dépôts de ce propriétaire de consulter (en lecture seule) les dépôts listés ci-dessous pendant lexécution des missions dActions (nécessite un jeton GITEA_TOKEN).",
"actions.general.cross_repo_desc": "Permet aux dépôts sélectionnés dêtre visible en lecture-seule par tous les dépôts de ce propriétaire à laide de GITEA_TOKEN lors de lexécution des tâches dactions.",
"actions.general.cross_repo_selected": "Dépôts sélectionnés",
"actions.general.cross_repo_target_repos": "Dépôts cibles",
"actions.general.cross_repo_add": "Ajouter un dépôt cible"

View File

@@ -3779,7 +3779,6 @@
"actions.runs.commit": "Tiomantas",
"actions.runs.run_details": "Sonraí Rith",
"actions.runs.workflow_file": "Comhad sreabhadh oibre",
"actions.runs.workflow_file_no_permission": "Gan cead chun an comhad sreafa oibre a fheiceáil",
"actions.runs.scheduled": "Sceidealaithe",
"actions.runs.pushed_by": "bhrú ag",
"actions.runs.invalid_workflow_helper": "Tá comhad cumraíochta sreabhadh oibre nebhailí. Seiceáil do chomhad cumraithe le do thoil: %s",
@@ -3836,33 +3835,7 @@
"actions.workflow.scope_owner": "Úinéir",
"actions.workflow.scope_global": "Domhanda",
"actions.workflow.required": "Riachtanach",
"actions.workflow.scoped_required_cannot_disable": "Tá an sreabhadh oibre raonaithe seo riachtanach agus ní féidir é a dhíchumasú.",
"actions.scoped_workflows": "Sreafaí Oibre Raonaithe",
"actions.scoped_workflows.desc_org": "Cláraigh stórtha mar fhoinsí sreabhadh oibre raonta. Ritheann comhaid sreabhadh oibre faoi eolairí sreabhadh oibre raonta brainse réamhshocraithe stórtha foinse ar gach stórtha den eagraíocht seo, i gcomhthéacs an stórtha sin féin.",
"actions.scoped_workflows.desc_user": "Cláraigh stórtha mar fhoinsí sreabhadh oibre raonta. Ritheann comhaid sreabhadh oibre faoi eolairí sreabhadh oibre raonta brainse réamhshocraithe stórtha foinse ar gach stór atá i do sheilbh, i gcomhthéacs an stórtha sin féin.",
"actions.scoped_workflows.desc_global": "Cláraigh stórtha mar fhoinsí sreabhadh oibre raonta. Ritheann comhaid sreabhadh oibre faoi eolairí sreabhadh oibre raonta brainse réamhshocraithe stórtha foinse ar gach stór ar an gcás seo, i gcomhthéacs an stórtha sin féin. Ós rud é go ndéantar foinsí ar leibhéal an chás a mheas ar imeachtaí gach stórtha, is féidir le clárú na gcomhad sin forchostais a chur leis ar chásanna móra.",
"actions.scoped_workflows.add_help": "Chun sreafaí oibre raonta a sholáthar ó stór, cuir na comhaid sreafa oibre faoi <code>%s</code> ar a bhrainse réamhshocraithe, agus ansin cláraigh an stór mar fhoinse thíos.",
"actions.scoped_workflows.security_note": "Déantar ábhar sreafa oibre stórais foinse a fhorghníomhú i ngach stórais lena mbaineann sé, agus scríobhtar a scripteanna céime agus a n-aschur chuig logaí Gníomhartha an stórais sin agus is féidir le duine ar bith ar féidir leo Gníomhartha an stórais ídigh a fheiceáil iad a léamh. Dá bhrí sin, nochtar loighic a sreafa oibre trí na logaí sin nuair a chláraítear stórais phríobháidigh mar fhoinse. Ní chláraítear ach stórais a bhféadfadh a n-ábhar sreafa oibre a bheith roinnte le gach stórais ídigh. Má thagraíonn sreabhadh oibre raonaithe do shreabhadh oibre in-athúsáidte ó stórais phríobháidigh, déan cinnte gur féidir le gach stórais ídigh é a léamh, nó teipfidh ar an sreabhadh oibre ansin.",
"actions.scoped_workflows.source.add": "Cuir stór foinse leis",
"actions.scoped_workflows.source.add_success": "Stór foinse curtha leis.",
"actions.scoped_workflows.source.remove_success": "Baineadh an stór foinse.",
"actions.scoped_workflows.source.not_found": "Níor aimsíodh an stórlann.",
"actions.scoped_workflows.required.update_success": "Sreafaí oibre riachtanacha nuashonraithe.",
"actions.scoped_workflows.required.label": "Marcáil sreafaí oibre mar riachtanacha (ní féidir le stórtha sreabhadh oibre riachtanach a dhíchumasú):",
"actions.scoped_workflows.required.patterns": "Patrúin seiceála stádais riachtanacha",
"actions.scoped_workflows.required.patterns_aria": "Patrúin seiceála stádais riachtanacha do %s",
"actions.scoped_workflows.required.patterns_note": "i bhfeidhm ach amháin nuair a bhíonn an sreabhadh oibre ag teastáil",
"actions.scoped_workflows.required.patterns_hint": "Marcáil an sreabhadh oibre mar is gá chun a phatrúin seiceála stádais a chumrú.",
"actions.scoped_workflows.required.patterns_help": "Patrún seiceála stádais amháin (glob) in aghaidh an líne. Ní féidir iarratas tarraingthe íditheach a chumasc ach amháin nuair a bheidh stádas a mheaitseálann gach patrún rite. Cuirtear é seo i bhfeidhm ar aon bhrainse sprice a bhfuil riail chosanta aige, fiú ceann a bhfuil a sheiceálacha stádais féin díchumasaithe; ní dhéantar geataíocht ar bhrainse sprice gan aon riail chosanta.",
"actions.scoped_workflows.required.patterns_empty": "Teastaíonn patrún seiceála stádais amháin ar a laghad ó gach sreabhadh oibre riachtanach.",
"actions.scoped_workflows.required.missing_file": "níl an comhad sa bhunleagan a thuilleadh",
"actions.scoped_workflows.required.expected_contexts": "Seiceálacha stádais ionchais (marcáiltear seic a mheaitseálann patrún)",
"actions.scoped_workflows.required.no_status_contexts": "Ní phostálann an sreabhadh oibre seo aon seiceálacha stádais, mar sin má mharcálann tú é mar riachtanas, chuirfeadh sé bac ar gach iarratas tarraingthe atá á úsáid ó chumasc. Díthiceáil Riachtanach.",
"actions.scoped_workflows.no_files": "Ní bhfuarthas aon chomhaid sreabha oibre raonaithe ar an mbrainse réamhshocraithe.",
"actions.workflow.run": "Rith Sreabhadh Oibre",
"actions.workflow.create_status_badge": "Cruthaigh suaitheantas stádais",
"actions.workflow.status_badge": "Suaitheantas Stádais",
"actions.workflow.status_badge_url": "URL suaitheantais",
"actions.workflow.not_found": "Níor aimsíodh sreabhadh oibre '%s'.",
"actions.workflow.run_success": "Ritheann sreabhadh oibre '%s' go rathúil.",
"actions.workflow.from_ref": "Úsáid sreabhadh oibre ó",

View File

@@ -2872,7 +2872,6 @@
"org.teams.visibility_limited_helper": "この組織のすべてのメンバーに表示されます。",
"org.teams.visibility_public": "公開",
"org.teams.visibility_public_helper": "サインインしているすべてのユーザーに表示されます。",
"org.teams.owners_visibility_fixed": "Ownersチームの公開範囲は変更できません。",
"org.teams.invite.title": "あなたは組織 <strong>%[2]s</strong> 内のチーム <strong>%[1]s</strong> への参加に招待されました。",
"org.teams.invite.by": "%s からの招待",
"org.teams.invite.description": "下のボタンをクリックしてチームに参加してください。",
@@ -3779,7 +3778,6 @@
"actions.runs.commit": "コミット",
"actions.runs.run_details": "実行の詳細",
"actions.runs.workflow_file": "ワークフローのファイル",
"actions.runs.workflow_file_no_permission": "ワークフローファイルを表示する権限がありません",
"actions.runs.scheduled": "スケジュール済み",
"actions.runs.pushed_by": "pushed by",
"actions.runs.invalid_workflow_helper": "ワークフロー設定ファイルは無効です。あなたの設定ファイルを確認してください: %s",
@@ -3836,12 +3834,7 @@
"actions.workflow.scope_owner": "オーナー",
"actions.workflow.scope_global": "グローバル",
"actions.workflow.required": "必須",
"actions.workflow.scoped_required_cannot_disable": "このスコープ付きワークフローは必須で、無効にできません。",
"actions.scoped_workflows": "スコープ付きワークフロー",
"actions.workflow.run": "ワークフローを実行",
"actions.workflow.create_status_badge": "ステータスバッジを作成する",
"actions.workflow.status_badge": "ステータスバッジ",
"actions.workflow.status_badge_url": "バッジURL",
"actions.workflow.not_found": "ワークフロー '%s' が見つかりません。",
"actions.workflow.run_success": "ワークフロー '%s' は正常に実行されました。",
"actions.workflow.from_ref": "使用するワークフローの取得元",

View File

@@ -1584,7 +1584,7 @@
"repo.issues.label_archived_filter": "아카이빙된 레이블 표시",
"repo.issues.label_archive_tooltip": "아카아빙된 레이블은 레이블로 검색할 때 기본적으로 제안 목록에서 제외됩니다.",
"repo.issues.label_exclusive_desc": "레이블명을 <code>스코프/항목</code>으로 지정하여 다른 <code>스코프/</code> 레이블과 상호 배타적으로 만드세요.",
"repo.issues.label_exclusive_warning": "이슈 또는 풀 리퀘스트의 레이블을 편집할 때 충돌하는 범위지정 레이블은 모두 제거됩니다.",
"repo.issues.label_exclusive_warning": "이슈 또는 풀 리퀘스트의 레이블을 편집할 때 충돌하는 스코프 지정 레이블은 모두 제거됩니다.",
"repo.issues.label_exclusive_order": "정렬 순서",
"repo.issues.label_exclusive_order_tooltip": "같은 스코프 내의 독점 레이블은 이 숫자 순서에 따라 정렬됩니다.",
"repo.issues.label_count": "레이블 %d개",
@@ -3820,12 +3820,7 @@
"actions.workflow.scope_owner": "소유자",
"actions.workflow.scope_global": "글로벌",
"actions.workflow.required": "필수 항목",
"actions.workflow.scoped_required_cannot_disable": "범위지정 워크플로우가 요구되며 비활성화할 수 없습니다.",
"actions.scoped_workflows": "범위지정 워크플로우",
"actions.workflow.run": "워크플로 실행",
"actions.workflow.create_status_badge": "상태 배지 생성",
"actions.workflow.status_badge": "상태 배지",
"actions.workflow.status_badge_url": "배지 URL",
"actions.workflow.not_found": "워크플로 '%s'를 찾을 수 없습니다.",
"actions.workflow.run_success": "워크플로 '%s'가 성공적으로 실행되었습니다.",
"actions.workflow.from_ref": "다음에서 워크플로 사용",

View File

@@ -3776,7 +3776,6 @@
"actions.runs.commit": "Cometimento",
"actions.runs.run_details": "Detalhes da execução",
"actions.runs.workflow_file": "Ficheiro de sequência de trabalho",
"actions.runs.workflow_file_no_permission": "Sem permissão para ver o ficheiro da sequência de trabalho",
"actions.runs.scheduled": "Agendadas",
"actions.runs.pushed_by": "enviado por",
"actions.runs.invalid_workflow_helper": "O ficheiro de configuração da sequência de trabalho é inválido. Verifique o seu ficheiro de configuração: %s",
@@ -3833,29 +3832,6 @@
"actions.workflow.scope_owner": "Proprietário(a)",
"actions.workflow.scope_global": "Global",
"actions.workflow.required": "Obrigatório",
"actions.workflow.scoped_required_cannot_disable": "Esta sequência de trabalho de âmbito específico é obrigatória e não pode ser desabilitada.",
"actions.scoped_workflows": "Sequências de trabalho de âmbito específico",
"actions.scoped_workflows.desc_org": "Registe repositórios como fontes de sequências de trabalho de âmbito específico. Os ficheiros das sequências de trabalho dentro de pastas das sequências de trabalho de âmbito específico do ramo principal de um repositório fonte são executados em todos os repositórios desta organização, no próprio contexto desse repositório.",
"actions.scoped_workflows.desc_user": "Registe repositórios como fontes de sequências de trabalho de âmbito específico. Os ficheiros das sequências de trabalho dentro de pastas das sequências de trabalho de âmbito específico do ramo principal de um repositório fonte são executados em todos os seus repositórios, no próprio contexto desse repositório.",
"actions.scoped_workflows.desc_global": "Registe repositórios como fontes de sequências de trabalho de âmbito específico. Os ficheiros das sequências de trabalho dentro de pastas das sequências de trabalho de âmbito específico do ramo principal de um repositório fonte são executados em todos os repositórios desta instância, no próprio contexto desse repositório. Uma vez que as fontes ao nível da instância são avaliadas em todos os eventos do repositório, registá-las poderá acrescentar uma sobrecarga em instâncias grandes.",
"actions.scoped_workflows.add_help": "Para fornecer sequências de trabalho de âmbito específico a partir de um repositório, cometa os ficheiros da sequência de trabalho sob <code>%s</code> no seu ramo principal e depois registe o repositório como uma fonte abaixo.",
"actions.scoped_workflows.security_note": "O conteúdo da sequência de trabalho de um repositório de origem é executado em todos os repositórios aos quais se aplica e os seus scripts de etapas, bem como os seus resultados, são escritos nos registos das operações desse repositório e podem ser lidos por qualquer pessoa que tenha permissão para ver as operações do repositório consumidor. Portanto, registar um repositório privado como uma fonte divulga a lógica da sequência de trabalho através desses registos. Registe apenas repositórios cujo conteúdo da sequência de trabalho possa ser partilhado com todos os repositórios consumidores. Se uma sequência de trabalho de âmbito específico fizer referência a uma sequência de trabalho reutilizável de um repositório privado, certifique-se que todos os repositórios consumidores a podem ler, caso contrário a sequência de trabalho irá falhar aí.",
"actions.scoped_workflows.source.add": "Adicionar repositório de origem",
"actions.scoped_workflows.source.add_success": "Repositório de origem adicionado.",
"actions.scoped_workflows.source.remove_success": "Repositório de origem removido.",
"actions.scoped_workflows.source.not_found": "Repositório não encontrado.",
"actions.scoped_workflows.required.update_success": "As sequências de trabalho obrigatórias foram refrescadas.",
"actions.scoped_workflows.required.label": "Marcar sequências de trabalho como sendo obrigatórias (uma sequência de trabalho obrigatória não pode ser desabilitada pelos repositórios):",
"actions.scoped_workflows.required.patterns": "Padrões de verificação de estado obrigatórios",
"actions.scoped_workflows.required.patterns_aria": "Padrões de verificação de estado obrigatórios para %s",
"actions.scoped_workflows.required.patterns_note": "aplicada apenas enquanto a sequência de trabalho for obrigatória",
"actions.scoped_workflows.required.patterns_hint": "Marque a sequência de trabalho como sendo obrigatória para configurar os seus padrões de verificação de estado.",
"actions.scoped_workflows.required.patterns_help": "Um padrão de verificação de estado (glob) por linha. Um pedido de integração consumidor só pode ser executado depois de ter sido aprovado um estado que corresponda a todos os padrões. Esta regra é aplicada a qualquer ramo de destino que tenha uma regra de salvaguarda, mesmo que as suas próprias verificações de estado estejam desabilitadas; um ramo de destino sem regra de salvaguarda não está sujeito a restrições.",
"actions.scoped_workflows.required.patterns_empty": "Cada sequência de trabalho obrigatória precisa de pelo menos um padrão de verificação de estado.",
"actions.scoped_workflows.required.missing_file": "o ficheiro já não está na origem",
"actions.scoped_workflows.required.expected_contexts": "Verificações de estado esperadas (está marcada uma verificação que corresponde a um padrão)",
"actions.scoped_workflows.required.no_status_contexts": "Esta sequência de trabalho não faz verificações de estado; por isso, marcá-la como obrigatória impediria a execução de todos os pedidos de integração que a utilizam. Desmarque a opção «Obrigatória».",
"actions.scoped_workflows.no_files": "Não foram encontrados quaisquer ficheiros de sequência de trabalho de âmbito específico no ramo principal.",
"actions.workflow.run": "Executar sequência de trabalho",
"actions.workflow.create_status_badge": "Criar distintivo de estado",
"actions.workflow.status_badge": "Distintivo de estado",

View File

@@ -3779,7 +3779,6 @@
"actions.runs.commit": "提交",
"actions.runs.run_details": "运行详情",
"actions.runs.workflow_file": "工作流文件",
"actions.runs.workflow_file_no_permission": "没有权限查看工作流文件",
"actions.runs.scheduled": "已计划的",
"actions.runs.pushed_by": "推送者",
"actions.runs.invalid_workflow_helper": "工作流配置文件无效。请检查您的配置文件:%s",
@@ -3836,33 +3835,7 @@
"actions.workflow.scope_owner": "拥有者",
"actions.workflow.scope_global": "全局",
"actions.workflow.required": "必须",
"actions.workflow.scoped_required_cannot_disable": "这个作用域工作流是必需的,不能被禁用。",
"actions.scoped_workflows": "作用域工作流",
"actions.scoped_workflows.desc_org": "将仓库注册为作用域工作流的源仓库。源仓库默认分支的「作用域工作流目录」中的工作流文件会在此组织中的每个仓库上运行,并使用对应仓库自身的上下文。",
"actions.scoped_workflows.desc_user": "将仓库注册为作用域工作流的源仓库。源仓库默认分支的「作用域工作流目录」中的工作流文件会在您拥有的每个仓库上运行,并使用对应仓库自身的上下文。",
"actions.scoped_workflows.desc_global": "将仓库注册为作用域工作流的源仓库。源仓库默认分支的「作用域工作流目录」中的工作流文件会在这个实例的每个仓库上运行,并使用对应仓库自身的上下文。由于实例级注册会针对每个仓库的事件进行检测,这在大型实例上可能会带来额外开销。",
"actions.scoped_workflows.add_help": "若要从仓库中提供作用域工作流,请在默认分支上提交工作流文件至 <code>%s</code> 然后将仓库注册为下面的源仓库。",
"actions.scoped_workflows.security_note": "一个源仓库的工作流会在它应用到的每个仓库上执行,它的 step 脚本和输出会被写到相关仓库的工作流日志里,并且能被任何有权查看这个仓库工作流的用户读取。因此,将一个私有仓库注册为源仓库会通过这些日志暴露工作流的逻辑。请仅注册那些工作流内容可以与所有相关仓库共享的仓库。如果一个作用域工作流从一个私有仓库引用了可复用工作流,请确保每个相关仓库都能读取它,否则该工作流会在这些仓库中运行失败。",
"actions.scoped_workflows.source.add": "添加源仓库",
"actions.scoped_workflows.source.add_success": "源仓库已添加。",
"actions.scoped_workflows.source.remove_success": "源仓库已删除。",
"actions.scoped_workflows.source.not_found": "未找到仓库。",
"actions.scoped_workflows.required.update_success": "「必需」工作流已更新。",
"actions.scoped_workflows.required.label": "把工作流标记为必需(「必需」工作流不能被仓库禁用):",
"actions.scoped_workflows.required.patterns": "「必需」状态检查表达式",
"actions.scoped_workflows.required.patterns_aria": "%s 的「必需」状态检查表达式",
"actions.scoped_workflows.required.patterns_note": "仅在工作流为「必需」时执行",
"actions.scoped_workflows.required.patterns_hint": "标记工作流为「必需」以配置它的状态检查表达式。",
"actions.scoped_workflows.required.patterns_help": "每行一个状态检查表达式glob。消费了这个工作流的合并请求只能在每条表达式都通过后才能合并。这仅适用于目标分支被分支保护规则保护的情况即使分支保护规则的状态检查被禁用。没有被分支保护规则保护的目标分支不会被限制。\n每行填写一个状态检查表达式glob。只有当每个表达式都有一个匹配且已通过的状态检查时相关的合并请求才能被合并。这仅适用于目标分支被分支保护规则保护的情况即使分支保护规则的状态检查被禁用。没有被分支保护规则保护的目标分支不会被限制。",
"actions.scoped_workflows.required.patterns_empty": "每个「必需」工作流都需要至少一个状态检查表达式。",
"actions.scoped_workflows.required.missing_file": "文件不存在于源仓库",
"actions.scoped_workflows.required.expected_contexts": "预期状态检查(匹配表达式的状态检查会被标记)",
"actions.scoped_workflows.required.no_status_contexts": "此工作流不会报告任何状态检查,将其标记为「必需」后所有相关的合并请求都将无法合并。请不要将其标记为「必需」。",
"actions.scoped_workflows.no_files": "默认分支上未找到任何作用域工作流文件。",
"actions.workflow.run": "运行工作流",
"actions.workflow.create_status_badge": "创建状态徽章",
"actions.workflow.status_badge": "状态徽章",
"actions.workflow.status_badge_url": "徽章 URL",
"actions.workflow.not_found": "未找到工作流「%s」。",
"actions.workflow.run_success": "工作流「%s」已成功运行。",
"actions.workflow.from_ref": "使用工作流从",

View File

@@ -50,7 +50,7 @@
"esbuild": "0.28.1",
"idiomorph": "0.7.4",
"jquery": "4.0.0",
"js-yaml": "5.1.0",
"js-yaml": "4.2.0",
"katex": "0.17.0",
"mermaid": "11.15.0",
"online-3d-viewer": "0.18.0",
@@ -70,6 +70,7 @@
"vite": "8.1.0",
"vite-string-plugin": "2.0.4",
"vue": "3.5.38",
"vue-bar-graph": "2.2.0",
"vue-chartjs": "5.3.3"
},
"devDependencies": {
@@ -80,8 +81,9 @@
"@stylistic/stylelint-plugin": "5.2.0",
"@types/codemirror": "5.60.17",
"@types/jquery": "4.0.1",
"@types/js-yaml": "4.0.9",
"@types/katex": "0.16.8",
"@types/node": "26.0.1",
"@types/node": "25.9.4",
"@types/pdfobject": "2.2.5",
"@types/sortablejs": "1.15.9",
"@types/swagger-ui-dist": "3.30.6",
@@ -90,13 +92,15 @@
"@typescript-eslint/parser": "8.62.0",
"@vitejs/plugin-vue": "6.0.7",
"@vitest/eslint-plugin": "1.6.20",
"eslint": "10.6.0",
"eslint": "10.5.0",
"eslint-import-resolver-typescript": "4.4.5",
"eslint-plugin-import-x": "4.17.1",
"eslint-plugin-array-func": "5.1.1",
"eslint-plugin-de-morgan": "2.1.2",
"eslint-plugin-import-x": "4.17.0",
"eslint-plugin-playwright": "2.10.4",
"eslint-plugin-regexp": "3.1.1",
"eslint-plugin-regexp": "3.1.0",
"eslint-plugin-sonarjs": "4.1.0",
"eslint-plugin-unicorn": "70.0.0",
"eslint-plugin-unicorn": "68.0.0",
"eslint-plugin-vue": "10.9.2",
"eslint-plugin-vue-scoped-css": "3.1.1",
"eslint-plugin-wc": "3.1.0",

324
pnpm-lock.yaml generated
View File

@@ -94,7 +94,7 @@ importers:
version: 2.6.2
'@vitejs/plugin-vue':
specifier: 6.0.7
version: 6.0.7(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))
version: 6.0.7(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))
ansi_up:
specifier: 6.0.6
version: 6.0.6
@@ -141,8 +141,8 @@ importers:
specifier: 4.0.0
version: 4.0.0
js-yaml:
specifier: 5.1.0
version: 5.1.0
specifier: 4.2.0
version: 4.2.0
katex:
specifier: 0.17.0
version: 0.17.0
@@ -193,20 +193,23 @@ importers:
version: 0.7.2
vite:
specifier: 8.1.0
version: 8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
version: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
vite-string-plugin:
specifier: 2.0.4
version: 2.0.4(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
version: 2.0.4(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
vue:
specifier: 3.5.38
version: 3.5.38(typescript@6.0.3)
vue-bar-graph:
specifier: 2.2.0
version: 2.2.0(typescript@6.0.3)
vue-chartjs:
specifier: 5.3.3
version: 5.3.3(chart.js@4.5.1)(vue@3.5.38(typescript@6.0.3))
devDependencies:
'@eslint-community/eslint-plugin-eslint-comments':
specifier: 4.7.2
version: 4.7.2(eslint@10.6.0(jiti@2.7.0))
version: 4.7.2(eslint@10.5.0(jiti@2.7.0))
'@eslint/json':
specifier: 2.0.0
version: 2.0.0
@@ -215,7 +218,7 @@ importers:
version: 1.61.1
'@stylistic/eslint-plugin':
specifier: 5.10.0
version: 5.10.0(eslint@10.6.0(jiti@2.7.0))
version: 5.10.0(eslint@10.5.0(jiti@2.7.0))
'@stylistic/stylelint-plugin':
specifier: 5.2.0
version: 5.2.0(stylelint@17.13.0(typescript@6.0.3))
@@ -225,12 +228,15 @@ importers:
'@types/jquery':
specifier: 4.0.1
version: 4.0.1
'@types/js-yaml':
specifier: 4.0.9
version: 4.0.9
'@types/katex':
specifier: 0.16.8
version: 0.16.8
'@types/node':
specifier: 26.0.1
version: 26.0.1
specifier: 25.9.4
version: 25.9.4
'@types/pdfobject':
specifier: 2.2.5
version: 2.2.5
@@ -248,40 +254,46 @@ importers:
version: 1.12.4
'@typescript-eslint/parser':
specifier: 8.62.0
version: 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
version: 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
'@vitest/eslint-plugin':
specifier: 1.6.20
version: 1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.9(@types/node@26.0.1)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)))
version: 1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.9(@types/node@25.9.4)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)))
eslint:
specifier: 10.6.0
version: 10.6.0(jiti@2.7.0)
specifier: 10.5.0
version: 10.5.0(jiti@2.7.0)
eslint-import-resolver-typescript:
specifier: 4.4.5
version: 4.4.5(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.6.0(jiti@2.7.0)))(eslint-plugin-import@2.32.0)(eslint@10.6.0(jiti@2.7.0))
version: 4.4.5(eslint-plugin-import-x@4.17.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0(jiti@2.7.0)))(eslint-plugin-import@2.32.0)(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-array-func:
specifier: 5.1.1
version: 5.1.1(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-de-morgan:
specifier: 2.1.2
version: 2.1.2(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-import-x:
specifier: 4.17.1
version: 4.17.1(@typescript-eslint/utils@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.6.0(jiti@2.7.0))
specifier: 4.17.0
version: 4.17.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-playwright:
specifier: 2.10.4
version: 2.10.4(eslint@10.6.0(jiti@2.7.0))
version: 2.10.4(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-regexp:
specifier: 3.1.1
version: 3.1.1(eslint@10.6.0(jiti@2.7.0))
specifier: 3.1.0
version: 3.1.0(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-sonarjs:
specifier: 4.1.0
version: 4.1.0(eslint@10.6.0(jiti@2.7.0))
version: 4.1.0(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-unicorn:
specifier: 70.0.0
version: 70.0.0(eslint@10.6.0(jiti@2.7.0))
specifier: 68.0.0
version: 68.0.0(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-vue:
specifier: 10.9.2
version: 10.9.2(@stylistic/eslint-plugin@5.10.0(eslint@10.6.0(jiti@2.7.0)))(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(vue-eslint-parser@10.4.0(eslint@10.6.0(jiti@2.7.0)))
version: 10.9.2(@stylistic/eslint-plugin@5.10.0(eslint@10.5.0(jiti@2.7.0)))(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(vue-eslint-parser@10.4.0(eslint@10.5.0(jiti@2.7.0)))
eslint-plugin-vue-scoped-css:
specifier: 3.1.1
version: 3.1.1(eslint@10.6.0(jiti@2.7.0))(vue-eslint-parser@10.4.0(eslint@10.6.0(jiti@2.7.0)))
version: 3.1.1(eslint@10.5.0(jiti@2.7.0))(vue-eslint-parser@10.4.0(eslint@10.5.0(jiti@2.7.0)))
eslint-plugin-wc:
specifier: 3.1.0
version: 3.1.0(eslint@10.6.0(jiti@2.7.0))
version: 3.1.0(eslint@10.5.0(jiti@2.7.0))
globals:
specifier: 17.7.0
version: 17.7.0
@@ -326,13 +338,13 @@ importers:
version: 6.0.3
typescript-eslint:
specifier: 8.62.0
version: 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
version: 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
updates:
specifier: 17.18.0
version: 17.18.0
vitest:
specifier: 4.1.9
version: 4.1.9(@types/node@26.0.1)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
version: 4.1.9(@types/node@25.9.4)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
vue-tsc:
specifier: 3.3.5
version: 3.3.5(typescript@6.0.3)
@@ -1301,6 +1313,9 @@ packages:
'@types/jquery@4.0.1':
resolution: {integrity: sha512-9a59A/tycXgYuPABcp6/3spSShn0NT2UOM4EfHvMumjYi4lJWTsK5SZWjhx3yRm9IHGCeWXdV2YfNsrWrft/CA==}
'@types/js-yaml@4.0.9':
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
'@types/jsdom@20.0.1':
resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==}
@@ -1319,8 +1334,8 @@ packages:
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
'@types/node@26.0.1':
resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==}
'@types/node@25.9.4':
resolution: {integrity: sha512-dszCsrKb5U7ZsVZBWiHFklTloVl0mSEnWH/iZXfZUlI4rzCUnsvGmgqfuVRHL54ugE7/wRuxEIXRa2iMZ+BG6g==}
'@types/pdfobject@2.2.5':
resolution: {integrity: sha512-7gD5tqc/RUDq0PyoLemL0vEHxBYi+zY0WVaFAx/Y0jBsXFgot1vB9No1GhDZGwRGJMCIZbgAb74QG9MTyTNU/g==}
@@ -2425,8 +2440,20 @@ packages:
eslint-import-resolver-webpack:
optional: true
eslint-plugin-import-x@4.17.1:
resolution: {integrity: sha512-4cdstYkKCyjumM2Q9NSI03K8D2a9F4Ssz33K2lv2hQa4KmR9jPLwk3uWGtNvclfqBrPGfGuMBwsGMbe6dMRbfg==}
eslint-plugin-array-func@5.1.1:
resolution: {integrity: sha512-TbVGk+yLqXHgtrS4DnYzg2Ycuk5y+lYFy5NgT748neQdJvNIYUucxp2QQjPU7dwbs9xp9fyktgtK069y9rNdig==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: '>=8.51.0'
eslint-plugin-de-morgan@2.1.2:
resolution: {integrity: sha512-6/MXP77FUrFGTdJ2Lny3Jv027bE3SUHDf+/VBUBckpH5TskOY5G4UX+AebqJeUpt3nXjy3S0KWBRwTItVQwgvw==}
engines: {node: ^20.0.0 || >=22.0.0}
peerDependencies:
eslint: ^8.45.0 || ^9.0.0 || ^10.0.0
eslint-plugin-import-x@4.17.0:
resolution: {integrity: sha512-aM7V25Bg6YuYxtEhwjafzfS0NTMds1D2PMQI0K4KqJxQJRtkP4CO+MQTWRdBq2qAnmPxTxLevhXUBtByxJqS1w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/utils': ^8.56.0
@@ -2454,8 +2481,8 @@ packages:
peerDependencies:
eslint: '>=8.40.0'
eslint-plugin-regexp@3.1.1:
resolution: {integrity: sha512-MxR5nqoQCtVWmJwia0D2+NlXX1xzdpkslsVOZLEYQ4PQWEaL65PCZXURxaBc3lPnkNFpNxzMIRmYVxdl8giXRA==}
eslint-plugin-regexp@3.1.0:
resolution: {integrity: sha512-qGXIC3DIKZHcK1H9A9+Byz9gmndY6TTSRkSMTZpNXdyCw2ObSehRgccJv35n9AdUakEjQp5VFNLas6BMXizCZg==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
peerDependencies:
eslint: '>=9.38.0'
@@ -2465,8 +2492,8 @@ packages:
peerDependencies:
eslint: ^8.0.0 || ^9.0.0 || ^10.0.0
eslint-plugin-unicorn@70.0.0:
resolution: {integrity: sha512-uAF9xMcVvvhTfvusCgogJ1wh4To3q2KhVMw3i1Apf/ILTbxsCjscvraAZACsEurb7no2fdXblD3whcbVnjw5zg==}
eslint-plugin-unicorn@68.0.0:
resolution: {integrity: sha512-mHYWvX948Q4H3bGc39bsNMxD/leOuNI+Iws9NVsoSz5VA7EGP86wnz7mZ/SPSvRhefT8L4hd8DHfDuGC+lIoCQ==}
engines: {node: '>=22'}
peerDependencies:
eslint: '>=10.4'
@@ -2520,8 +2547,8 @@ packages:
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
eslint@10.6.0:
resolution: {integrity: sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==}
eslint@10.5.0:
resolution: {integrity: sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
hasBin: true
peerDependencies:
@@ -3041,10 +3068,6 @@ packages:
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
hasBin: true
js-yaml@5.1.0:
resolution: {integrity: sha512-s8VA5jkR8f22S3NAXmhKPFqGUduqZGlsufabVOgN14iTdw/RXcym7bKkbwjxLK9Yw2lEvvmJjFp119+KPeo8Kg==}
hasBin: true
jsdoc-type-pratt-parser@7.2.0:
resolution: {integrity: sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==}
engines: {node: '>=20.0.0'}
@@ -4161,8 +4184,8 @@ packages:
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
engines: {node: '>= 0.4'}
undici-types@8.3.0:
resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==}
undici-types@7.24.6:
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
unicorn-magic@0.4.0:
resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==}
@@ -4294,6 +4317,9 @@ packages:
vscode-uri@3.1.0:
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
vue-bar-graph@2.2.0:
resolution: {integrity: sha512-1xFPho2nM6nFDziExLu48vKO+Q90gjxz1NyHfc+MhgfYDSxR9BMyhOIXUO5EmwKIVEX5dBoP2n3Ius8SjKRD4g==}
vue-chartjs@5.3.3:
resolution: {integrity: sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA==}
peerDependencies:
@@ -4913,15 +4939,15 @@ snapshots:
'@esbuild/win32-x64@0.28.1':
optional: true
'@eslint-community/eslint-plugin-eslint-comments@4.7.2(eslint@10.6.0(jiti@2.7.0))':
'@eslint-community/eslint-plugin-eslint-comments@4.7.2(eslint@10.5.0(jiti@2.7.0))':
dependencies:
escape-string-regexp: 4.0.0
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
ignore: 7.0.5
'@eslint-community/eslint-utils@4.9.1(eslint@10.6.0(jiti@2.7.0))':
'@eslint-community/eslint-utils@4.9.1(eslint@10.5.0(jiti@2.7.0))':
dependencies:
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
@@ -4997,14 +5023,14 @@ snapshots:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/node': 26.0.1
'@types/node': 25.9.4
jest-mock: 29.7.0
'@jest/fake-timers@29.7.0':
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
'@types/node': 26.0.1
'@types/node': 25.9.4
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -5018,7 +5044,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 26.0.1
'@types/node': 25.9.4
'@types/yargs': 17.0.35
chalk: 4.1.2
@@ -5331,11 +5357,11 @@ snapshots:
'@standard-schema/spec@1.1.0': {}
'@stylistic/eslint-plugin@5.10.0(eslint@10.6.0(jiti@2.7.0))':
'@stylistic/eslint-plugin@5.10.0(eslint@10.5.0(jiti@2.7.0))':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0))
'@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0))
'@typescript-eslint/types': 8.62.0
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
eslint-visitor-keys: 4.2.1
espree: 10.4.0
estraverse: 5.3.0
@@ -5515,9 +5541,11 @@ snapshots:
'@types/jquery@4.0.1': {}
'@types/js-yaml@4.0.9': {}
'@types/jsdom@20.0.1':
dependencies:
'@types/node': 26.0.1
'@types/node': 25.9.4
'@types/tough-cookie': 4.0.5
parse5: 7.3.0
@@ -5532,9 +5560,9 @@ snapshots:
'@types/ms@2.1.0': {}
'@types/node@26.0.1':
'@types/node@25.9.4':
dependencies:
undici-types: 8.3.0
undici-types: 7.24.6
'@types/pdfobject@2.2.5': {}
@@ -5563,7 +5591,7 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
'@types/node': 26.0.1
'@types/node': 25.9.4
'@types/yargs-parser@21.0.3': {}
@@ -5571,15 +5599,15 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
'@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)':
'@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/parser': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/scope-manager': 8.62.0
'@typescript-eslint/type-utils': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/utils': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/type-utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.62.0
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@6.0.3)
@@ -5587,14 +5615,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)':
'@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.62.0
'@typescript-eslint/types': 8.62.0
'@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.62.0
debug: 4.4.3
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
@@ -5617,13 +5645,13 @@ snapshots:
dependencies:
typescript: 6.0.3
'@typescript-eslint/type-utils@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)':
'@typescript-eslint/type-utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@typescript-eslint/types': 8.62.0
'@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3)
'@typescript-eslint/utils': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
debug: 4.4.3
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
ts-api-utils: 2.5.0(typescript@6.0.3)
typescript: 6.0.3
transitivePeerDependencies:
@@ -5646,13 +5674,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)':
'@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0))
'@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0))
'@typescript-eslint/scope-manager': 8.62.0
'@typescript-eslint/types': 8.62.0
'@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3)
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
@@ -5737,21 +5765,21 @@ snapshots:
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
'@vitejs/plugin-vue@6.0.7(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))':
'@vitejs/plugin-vue@6.0.7(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))':
dependencies:
'@rolldown/pluginutils': 1.0.1
vite: 8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
vite: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
vue: 3.5.38(typescript@6.0.3)
'@vitest/eslint-plugin@1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.9(@types/node@26.0.1)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)))':
'@vitest/eslint-plugin@1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.9(@types/node@25.9.4)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)))':
dependencies:
'@typescript-eslint/scope-manager': 8.62.0
'@typescript-eslint/utils': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
eslint: 10.6.0(jiti@2.7.0)
'@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
eslint: 10.5.0(jiti@2.7.0)
optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
typescript: 6.0.3
vitest: 4.1.9(@types/node@26.0.1)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
vitest: 4.1.9(@types/node@25.9.4)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
transitivePeerDependencies:
- supports-color
@@ -5764,13 +5792,13 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.1.0
'@vitest/mocker@4.1.9(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))':
'@vitest/mocker@4.1.9(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))':
dependencies:
'@vitest/spy': 4.1.9
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
vite: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
'@vitest/pretty-format@4.1.9':
dependencies:
@@ -6047,7 +6075,7 @@ snapshots:
buffer-image-size@0.6.4:
dependencies:
'@types/node': 26.0.1
'@types/node': 25.9.4
buffer@5.7.1:
dependencies:
@@ -6737,10 +6765,10 @@ snapshots:
- supports-color
optional: true
eslint-import-resolver-typescript@4.4.5(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.6.0(jiti@2.7.0)))(eslint-plugin-import@2.32.0)(eslint@10.6.0(jiti@2.7.0)):
eslint-import-resolver-typescript@4.4.5(eslint-plugin-import-x@4.17.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0(jiti@2.7.0)))(eslint-plugin-import@2.32.0)(eslint@10.5.0(jiti@2.7.0)):
dependencies:
debug: 4.4.3
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
eslint-import-context: 0.1.9(unrs-resolver@1.12.2)
get-tsconfig: 4.14.0
is-bun-module: 2.0.0
@@ -6748,29 +6776,37 @@ snapshots:
tinyglobby: 0.2.17
unrs-resolver: 1.12.2
optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@4.4.5)(eslint@10.6.0(jiti@2.7.0))
eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.6.0(jiti@2.7.0))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@4.4.5)(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-import-x: 4.17.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0(jiti@2.7.0))
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.13.0(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.5)(eslint@10.6.0(jiti@2.7.0)):
eslint-module-utils@2.13.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.5)(eslint@10.5.0(jiti@2.7.0)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
eslint: 10.6.0(jiti@2.7.0)
'@typescript-eslint/parser': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
eslint: 10.5.0(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10
eslint-import-resolver-typescript: 4.4.5(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.6.0(jiti@2.7.0)))(eslint-plugin-import@2.32.0)(eslint@10.6.0(jiti@2.7.0))
eslint-import-resolver-typescript: 4.4.5(eslint-plugin-import-x@4.17.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0(jiti@2.7.0)))(eslint-plugin-import@2.32.0)(eslint@10.5.0(jiti@2.7.0))
transitivePeerDependencies:
- supports-color
optional: true
eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.6.0(jiti@2.7.0)):
eslint-plugin-array-func@5.1.1(eslint@10.5.0(jiti@2.7.0)):
dependencies:
eslint: 10.5.0(jiti@2.7.0)
eslint-plugin-de-morgan@2.1.2(eslint@10.5.0(jiti@2.7.0)):
dependencies:
eslint: 10.5.0(jiti@2.7.0)
eslint-plugin-import-x@4.17.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0(jiti@2.7.0)):
dependencies:
'@typescript-eslint/types': 8.62.0
comment-parser: 1.4.7
debug: 4.4.3
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
eslint-import-context: 0.1.9(unrs-resolver@1.12.2)
is-glob: 4.0.3
minimatch: 10.2.5
@@ -6778,12 +6814,12 @@ snapshots:
stable-hash-x: 0.2.0
unrs-resolver: 1.12.2
optionalDependencies:
'@typescript-eslint/utils': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
eslint-import-resolver-node: 0.3.10
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@4.4.5)(eslint@10.6.0(jiti@2.7.0)):
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@4.4.5)(eslint@10.5.0(jiti@2.7.0)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -6792,9 +6828,9 @@ snapshots:
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10
eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.5)(eslint@10.6.0(jiti@2.7.0))
eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.5)(eslint@10.5.0(jiti@2.7.0))
hasown: 2.0.4
is-core-module: 2.16.2
is-glob: 4.0.3
@@ -6806,35 +6842,35 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/parser': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
optional: true
eslint-plugin-playwright@2.10.4(eslint@10.6.0(jiti@2.7.0)):
eslint-plugin-playwright@2.10.4(eslint@10.5.0(jiti@2.7.0)):
dependencies:
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
globals: 17.7.0
eslint-plugin-regexp@3.1.1(eslint@10.6.0(jiti@2.7.0)):
eslint-plugin-regexp@3.1.0(eslint@10.5.0(jiti@2.7.0)):
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0))
'@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0))
'@eslint-community/regexpp': 4.12.2
comment-parser: 1.4.7
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
jsdoc-type-pratt-parser: 7.2.0
refa: 0.12.1
regexp-ast-analysis: 0.7.1
scslre: 0.3.0
eslint-plugin-sonarjs@4.1.0(eslint@10.6.0(jiti@2.7.0)):
eslint-plugin-sonarjs@4.1.0(eslint@10.5.0(jiti@2.7.0)):
dependencies:
'@eslint-community/regexpp': 4.12.2
builtin-modules: 3.3.0
bytes: 3.1.2
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
functional-red-black-tree: 1.0.1
globals: 17.7.0
jsx-ast-utils-x: 0.1.0
@@ -6846,16 +6882,16 @@ snapshots:
typescript: 6.0.3
yaml: 2.9.0
eslint-plugin-unicorn@70.0.0(eslint@10.6.0(jiti@2.7.0)):
eslint-plugin-unicorn@68.0.0(eslint@10.5.0(jiti@2.7.0)):
dependencies:
'@babel/helper-validator-identifier': 7.29.7
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0))
'@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0))
browserslist: 4.28.2
change-case: 5.4.4
ci-info: 4.4.0
core-js-compat: 3.49.0
detect-indent: 7.0.2
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
find-up-simple: 1.0.1
globals: 17.7.0
indent-string: 5.0.0
@@ -6866,33 +6902,33 @@ snapshots:
semver: 7.8.4
strip-indent: 4.1.1
eslint-plugin-vue-scoped-css@3.1.1(eslint@10.6.0(jiti@2.7.0))(vue-eslint-parser@10.4.0(eslint@10.6.0(jiti@2.7.0))):
eslint-plugin-vue-scoped-css@3.1.1(eslint@10.5.0(jiti@2.7.0))(vue-eslint-parser@10.4.0(eslint@10.5.0(jiti@2.7.0))):
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0))
'@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0))
es-toolkit: 1.47.0
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
postcss: 8.5.15
postcss-safe-parser: 7.0.1(postcss@8.5.15)
postcss-selector-parser: 7.1.1
vue-eslint-parser: 10.4.0(eslint@10.6.0(jiti@2.7.0))
vue-eslint-parser: 10.4.0(eslint@10.5.0(jiti@2.7.0))
eslint-plugin-vue@10.9.2(@stylistic/eslint-plugin@5.10.0(eslint@10.6.0(jiti@2.7.0)))(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(vue-eslint-parser@10.4.0(eslint@10.6.0(jiti@2.7.0))):
eslint-plugin-vue@10.9.2(@stylistic/eslint-plugin@5.10.0(eslint@10.5.0(jiti@2.7.0)))(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(vue-eslint-parser@10.4.0(eslint@10.5.0(jiti@2.7.0))):
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0))
eslint: 10.6.0(jiti@2.7.0)
'@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0))
eslint: 10.5.0(jiti@2.7.0)
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 7.1.1
semver: 7.8.4
vue-eslint-parser: 10.4.0(eslint@10.6.0(jiti@2.7.0))
vue-eslint-parser: 10.4.0(eslint@10.5.0(jiti@2.7.0))
xml-name-validator: 4.0.0
optionalDependencies:
'@stylistic/eslint-plugin': 5.10.0(eslint@10.6.0(jiti@2.7.0))
'@typescript-eslint/parser': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@stylistic/eslint-plugin': 5.10.0(eslint@10.5.0(jiti@2.7.0))
'@typescript-eslint/parser': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
eslint-plugin-wc@3.1.0(eslint@10.6.0(jiti@2.7.0)):
eslint-plugin-wc@3.1.0(eslint@10.5.0(jiti@2.7.0)):
dependencies:
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
is-valid-element-name: 1.0.0
js-levenshtein-esm: 2.0.0
@@ -6909,9 +6945,9 @@ snapshots:
eslint-visitor-keys@5.0.1: {}
eslint@10.6.0(jiti@2.7.0):
eslint@10.5.0(jiti@2.7.0):
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0))
'@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.23.5
'@eslint/config-helpers': 0.6.0
@@ -7163,7 +7199,7 @@ snapshots:
happy-dom@20.10.6:
dependencies:
'@types/node': 26.0.1
'@types/node': 25.9.4
'@types/whatwg-mimetype': 3.0.2
'@types/ws': 8.18.1
buffer-image-size: 0.6.4
@@ -7450,7 +7486,7 @@ snapshots:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/jsdom': 20.0.1
'@types/node': 26.0.1
'@types/node': 25.9.4
jest-mock: 29.7.0
jest-util: 29.7.0
jsdom: 20.0.3
@@ -7474,13 +7510,13 @@ snapshots:
jest-mock@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 26.0.1
'@types/node': 25.9.4
jest-util: 29.7.0
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 26.0.1
'@types/node': 25.9.4
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -7502,10 +7538,6 @@ snapshots:
dependencies:
argparse: 2.0.1
js-yaml@5.1.0:
dependencies:
argparse: 2.0.1
jsdoc-type-pratt-parser@7.2.0: {}
jsdom@20.0.3:
@@ -8828,13 +8860,13 @@ snapshots:
reflect.getprototypeof: 1.0.10
optional: true
typescript-eslint@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3):
typescript-eslint@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3):
dependencies:
'@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/parser': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/parser': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3)
'@typescript-eslint/utils': 8.62.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
eslint: 10.6.0(jiti@2.7.0)
'@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)
eslint: 10.5.0(jiti@2.7.0)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
@@ -8855,7 +8887,7 @@ snapshots:
which-boxed-primitive: 1.1.1
optional: true
undici-types@8.3.0: {}
undici-types@7.24.6: {}
unicorn-magic@0.4.0: {}
@@ -8911,11 +8943,11 @@ snapshots:
vanilla-colorful@0.7.2: {}
vite-string-plugin@2.0.4(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)):
vite-string-plugin@2.0.4(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)):
dependencies:
vite: 8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
vite: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0):
vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -8923,16 +8955,16 @@ snapshots:
rolldown: 1.1.3
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 26.0.1
'@types/node': 25.9.4
esbuild: 0.28.1
fsevents: 2.3.3
jiti: 2.7.0
yaml: 2.9.0
vitest@4.1.9(@types/node@26.0.1)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)):
vitest@4.1.9(@types/node@25.9.4)(happy-dom@20.10.6)(jsdom@20.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)):
dependencies:
'@vitest/expect': 4.1.9
'@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
'@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
'@vitest/pretty-format': 4.1.9
'@vitest/runner': 4.1.9
'@vitest/snapshot': 4.1.9
@@ -8949,10 +8981,10 @@ snapshots:
tinyexec: 1.2.4
tinyglobby: 0.2.17
tinyrainbow: 3.1.0
vite: 8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
vite: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 26.0.1
'@types/node': 25.9.4
happy-dom: 20.10.6
jsdom: 20.0.3
transitivePeerDependencies:
@@ -8960,15 +8992,21 @@ snapshots:
vscode-uri@3.1.0: {}
vue-bar-graph@2.2.0(typescript@6.0.3):
dependencies:
vue: 3.5.38(typescript@6.0.3)
transitivePeerDependencies:
- typescript
vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.38(typescript@6.0.3)):
dependencies:
chart.js: 4.5.1
vue: 3.5.38(typescript@6.0.3)
vue-eslint-parser@10.4.0(eslint@10.6.0(jiti@2.7.0)):
vue-eslint-parser@10.4.0(eslint@10.5.0(jiti@2.7.0)):
dependencies:
debug: 4.4.3
eslint: 10.6.0(jiti@2.7.0)
eslint: 10.5.0(jiti@2.7.0)
eslint-scope: 9.1.2
eslint-visitor-keys: 5.0.1
espree: 11.2.0

View File

@@ -73,11 +73,7 @@
"postUpdateOptions": ["gomodUpdateImportPaths"],
"postUpgradeTasks": {
"commands": ["make tidy"],
"fileFilters": [
"go.mod",
"go.sum",
"assets/go-licenses.json",
],
"fileFilters": ["go.mod", "go.sum", "assets/go-licenses.json"],
"executionMode": "branch",
},
},
@@ -99,15 +95,8 @@
"matchManagers": ["npm"],
"postUpdateOptions": ["pnpmDedupe"],
"postUpgradeTasks": {
"commands": ["make svg", "make generate-codemirror-languages"],
"fileFilters": [
"package.json",
"pnpm-lock.yaml",
"pnpm-workspace.yaml",
"public/assets/img/svg/**",
"options/fileicon/**",
"assets/codemirror-languages.json",
],
"commands": ["make svg"],
"fileFilters": ["package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml", "public/assets/img/svg/**", "options/fileicon/**"],
"executionMode": "branch",
},
},

View File

@@ -258,27 +258,8 @@ func GetAllCommits(ctx *context.APIContext) {
ctx.APIErrorInternal(err)
return
} else if commitsCountTotal == 0 {
// when date filters are active, a zero count may just mean no
// commits in the requested range — not that the path is invalid
if since == "" && until == "" {
ctx.APIErrorNotFound()
return
}
// verify the path actually exists in the revision history
totalWithoutDate, err := gitrepo.CommitsCount(ctx, ctx.Repo.Repository,
gitrepo.CommitsCountOptions{
Not: not,
Revision: []string{sha},
RelPath: []string{path},
})
if err != nil {
ctx.APIErrorInternal(err)
return
}
if totalWithoutDate == 0 {
ctx.APIErrorNotFound()
return
}
ctx.APIErrorNotFound()
return
}
commits, _, err = ctx.Repo.GitRepo.CommitsByFileAndRange(

View File

@@ -12,6 +12,13 @@ type swaggerResponseSecretList struct {
Body []api.Secret `json:"body"`
}
// Secret
// swagger:response Secret
type swaggerResponseSecret struct {
// in:body
Body api.Secret `json:"body"`
}
// ActionVariable
// swagger:response ActionVariable
type swaggerResponseActionVariable struct {

View File

@@ -98,6 +98,13 @@ type swaggerIssueTemplates struct {
Body []api.IssueTemplate `json:"body"`
}
// StopWatch
// swagger:response StopWatch
type swaggerResponseStopWatch struct {
// in:body
Body api.StopWatch `json:"body"`
}
// StopWatchList
// swagger:response StopWatchList
type swaggerResponseStopWatchList struct {

View File

@@ -0,0 +1,15 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package swagger
import (
api "gitea.dev/modules/structs"
)
// NodeInfo
// swagger:response NodeInfo
type swaggerResponseNodeInfo struct {
// in:body
Body api.NodeInfo `json:"body"`
}

View File

@@ -8,12 +8,11 @@ import (
"gitea.dev/services/forms"
)
// not actually a set of parameters, just a hack to get go-swagger to include
// definitions of the various XYZOption structs. The annotation below uses an
// operation id that matches no route, so the spec carries no unused response
// or parameter for it while still emitting the referenced definitions.
// not actually a response, just a hack to get go-swagger to include definitions
// of the various XYZOption structs
// swagger:parameters parameterBodies
// parameterBodies
// swagger:response parameterBodies
type swaggerParameterBodies struct {
// in:body
AddCollaboratorOption api.AddCollaboratorOption
@@ -236,7 +235,4 @@ type swaggerParameterBodies struct {
// in:body
LockIssueOption api.LockIssueOption
// in:body
MergeUpstreamRequest api.MergeUpstreamRequest
}

View File

@@ -84,6 +84,13 @@ type swaggerResponseTagProtection struct {
Body api.TagProtection `json:"body"`
}
// Reference
// swagger:response Reference
type swaggerResponseReference struct {
// in:body
Body api.Reference `json:"body"`
}
// ReferenceList
// swagger:response ReferenceList
type swaggerResponseReferenceList struct {
@@ -504,6 +511,12 @@ type swaggerCompare struct {
Body api.Compare `json:"body"`
}
// swagger:response MergeUpstreamRequest
type swaggerMergeUpstreamRequest struct {
// in:body
Body api.MergeUpstreamRequest `json:"body"`
}
// swagger:response MergeUpstreamResponse
type swaggerMergeUpstreamResponse struct {
// in:body

View File

@@ -147,9 +147,6 @@ func MustInitSessioner() func(next http.Handler) http.Handler {
Secure: setting.SessionConfig.Secure,
SameSite: setting.SessionConfig.SameSite,
Domain: setting.SessionConfig.Domain,
// in the future, if websocket is used, the websocket handler should manage its own session sync (release)
IgnoreReleaseForWebSocket: true,
})
if err != nil {
log.Fatal("common.Sessioner failed: %v", err)

View File

@@ -31,7 +31,9 @@ import (
type preReceiveContext struct {
*gitea_context.PrivateContext
user *user_model.User // the "pusher", it's the org user if a DeployKey is used
// loadedPusher indicates that where the following information are loaded
loadedPusher bool
user *user_model.User // it's the org user if a DeployKey is used
userPerm access_model.Permission
deployKeyAccessMode perm_model.AccessMode
@@ -51,7 +53,10 @@ type preReceiveContext struct {
func (ctx *preReceiveContext) canWriteCodeUnit() bool {
if ctx.canWriteCodeUnitCached == nil {
canWrite := ctx.userPerm.CanWrite(unit.TypeCode) || ctx.deployKeyAccessMode >= perm_model.AccessModeWrite
var canWrite bool
if ctx.loadPusherAndPermission() {
canWrite = ctx.userPerm.CanWrite(unit.TypeCode) || ctx.deployKeyAccessMode >= perm_model.AccessModeWrite
}
ctx.canWriteCodeUnitCached = &canWrite
}
return *ctx.canWriteCodeUnitCached
@@ -86,6 +91,9 @@ func (ctx *preReceiveContext) assertCanWriteRef(refFullName git.RefName) bool {
// CanCreatePullRequest returns true if pusher can create pull requests
func (ctx *preReceiveContext) CanCreatePullRequest() bool {
if !ctx.checkedCanCreatePullRequest {
if !ctx.loadPusherAndPermission() {
return false
}
ctx.canCreatePullRequest = ctx.userPerm.CanRead(unit.TypePullRequests)
ctx.checkedCanCreatePullRequest = true
}
@@ -116,10 +124,6 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) {
opts: opts,
}
if !ourCtx.loadPusherAndPermission() {
return // if error occurs, loadPusherAndPermission had written the error response
}
// Iterate across the provided old commit IDs
for i := range opts.OldCommitIDs {
oldCommitID := opts.OldCommitIDs[i]
@@ -277,10 +281,18 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
canPush = !changedProtectedfiles && protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)
}
} else {
user, err := user_model.GetUserByID(ctx, ctx.opts.UserID)
if err != nil {
log.Error("Unable to GetUserByID for commits from %s to %s in %-v: %v", oldCommitID, newCommitID, repo, err)
ctx.JSON(http.StatusInternalServerError, private.Response{
Err: fmt.Sprintf("Unable to GetUserByID for commits from %s to %s: %v", oldCommitID, newCommitID, err),
})
return
}
if isForcePush {
canPush = !changedProtectedfiles && protectBranch.CanUserForcePush(ctx, ctx.user)
canPush = !changedProtectedfiles && protectBranch.CanUserForcePush(ctx, user)
} else {
canPush = !changedProtectedfiles && protectBranch.CanUserPush(ctx, ctx.user)
canPush = !changedProtectedfiles && protectBranch.CanUserPush(ctx, user)
}
}
@@ -342,6 +354,12 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
return
}
// although we should have called `loadPusherAndPermission` before, here we call it explicitly again because we need to access ctx.user below
if !ctx.loadPusherAndPermission() {
// if error occurs, loadPusherAndPermission had written the error response
return
}
// Now check if the user is allowed to merge PRs for this repository
// Note: we can use ctx.perm and ctx.user directly as they will have been loaded above
allowedMerge, err := pull_service.IsUserAllowedToMerge(ctx, pr, ctx.userPerm, ctx.user)
@@ -481,6 +499,10 @@ func generateGitEnv(opts *private.HookOptions) (env []string) {
// loadPusherAndPermission returns false if an error occurs, and it writes the error response
func (ctx *preReceiveContext) loadPusherAndPermission() bool {
if ctx.loadedPusher {
return true
}
if ctx.opts.UserID == user_model.ActionsUserID {
taskID := ctx.opts.ActionsTaskID
ctx.user = user_model.NewActionsUserWithTaskID(taskID)
@@ -533,5 +555,7 @@ func (ctx *preReceiveContext) loadPusherAndPermission() bool {
}
ctx.deployKeyAccessMode = deployKey.Mode
}
ctx.loadedPusher = true
return true
}

View File

@@ -52,6 +52,7 @@ func TestPreReceiveCanWriteCodePerBranch(t *testing.T) {
mockCtx, _ := contexttest.MockPrivateContext(t, "/")
ctx := &preReceiveContext{
PrivateContext: mockCtx,
loadedPusher: true,
user: maintainer,
userPerm: headPerm,
}

View File

@@ -450,9 +450,27 @@ func EditUserPost(ctx *context.Context) {
log.Trace("Account profile updated by admin (%s): %s", ctx.Doer.Name, u.Name)
if form.Reset2FA {
if _, _, err := auth.DisableTwoFactor(ctx, u.ID); err != nil {
ctx.ServerError("auth.DisableTwoFactor", err)
tf, err := auth.GetTwoFactorByUID(ctx, u.ID)
if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
ctx.ServerError("auth.GetTwoFactorByUID", err)
return
} else if tf != nil {
if err := auth.DeleteTwoFactorByID(ctx, tf.ID, u.ID); err != nil {
ctx.ServerError("auth.DeleteTwoFactorByID", err)
return
}
}
wn, err := auth.GetWebAuthnCredentialsByUID(ctx, u.ID)
if err != nil {
ctx.ServerError("auth.GetTwoFactorByUID", err)
return
}
for _, cred := range wn {
if _, err := auth.DeleteCredential(ctx, cred.ID, u.ID); err != nil {
ctx.ServerError("auth.DeleteCredential", err)
return
}
}
}

View File

@@ -118,7 +118,7 @@ func autoSignIn(ctx *context.Context) (bool, error) {
ctx.SetSiteCookie(setting.CookieRememberName, nt.ID+":"+token, setting.LogInRememberDays*timeutil.Day)
if err := regenerateSession(ctx, nil, map[string]any{
if err := updateSession(ctx, nil, map[string]any{
session.KeyUID: u.ID,
session.KeyUname: u.Name,
session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
@@ -357,7 +357,7 @@ func SignInPost(ctx *context.Context) {
// User will need to use WebAuthn, save data
updates["totpEnrolled"] = u.ID
}
if err := regenerateSession(ctx, nil, updates); err != nil {
if err := updateSession(ctx, nil, updates); err != nil {
ctx.ServerError("UserSignIn: Unable to update session", err)
return
}
@@ -398,7 +398,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember bool) {
return
}
if err := regenerateSession(ctx, []string{
if err := updateSession(ctx, []string{
// Delete the openid, 2fa and link_account data
"openid_verified_uri",
"openid_signin_remember",
@@ -884,7 +884,7 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) {
log.Trace("User activated: %s", user.Name)
if err := regenerateSession(ctx, nil, map[string]any{
if err := updateSession(ctx, nil, map[string]any{
"uid": user.ID,
"uname": user.Name,
}); err != nil {
@@ -936,7 +936,7 @@ func ActivateEmail(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
}
func regenerateSession(ctx *context.Context, deletes []string, updates map[string]any) error {
func updateSession(ctx *context.Context, deletes []string, updates map[string]any) error {
if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
return fmt.Errorf("regenerate session: %w", err)
}

View File

@@ -164,12 +164,7 @@ func oauth2LinkAccount(ctx *context.Context, u *user_model.User, linkAccountData
return
}
if err := Oauth2SetLinkAccountData(ctx, *linkAccountData); err != nil {
ctx.ServerError("Oauth2SetLinkAccountData", err)
return
}
if err := regenerateSession(ctx, nil, map[string]any{
if err := updateSession(ctx, nil, map[string]any{
// User needs to use 2FA, save data and redirect to 2FA page.
"twofaUid": u.ID,
"twofaRemember": remember,

View File

@@ -285,7 +285,9 @@ func oauth2GetLinkAccountData(ctx *context.Context) *LinkAccountData {
}
func Oauth2SetLinkAccountData(ctx *context.Context, linkAccountData LinkAccountData) error {
return ctx.Session.Set("linkAccountData", linkAccountData)
return updateSession(ctx, nil, map[string]any{
"linkAccountData": linkAccountData,
})
}
func showLinkingLogin(ctx *context.Context, authSourceID int64, gothUser goth.User) {
@@ -407,7 +409,7 @@ func handleOAuth2SignIn(ctx *context.Context, authSource *auth.Source, u *user_m
return
}
if err := regenerateSession(ctx, nil, map[string]any{
if err := updateSession(ctx, nil, map[string]any{
session.KeyUID: u.ID,
session.KeyUname: u.Name,
session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
@@ -432,7 +434,7 @@ func handleOAuth2SignIn(ctx *context.Context, authSource *auth.Source, u *user_m
}
}
if err := regenerateSession(ctx, nil, map[string]any{
if err := updateSession(ctx, nil, map[string]any{
// User needs to use 2FA, save data and redirect to 2FA page.
"twofaUid": u.ID,
"twofaRemember": false,

View File

@@ -213,7 +213,7 @@ func signInOpenIDVerify(ctx *context.Context) {
if u != nil {
nickname = u.LowerName
}
if err := regenerateSession(ctx, nil, map[string]any{
if err := updateSession(ctx, nil, map[string]any{
"openid_verified_uri": id,
"openid_determined_email": email,
"openid_determined_username": nickname,

View File

@@ -186,22 +186,6 @@ func ServeAttachment(ctx *context.Context, uuid string) {
return
}
// Draft release attachments must not be exposed to anyone without write
// access, matching the API-side canAccessReleaseDraft gate. Otherwise the
// UUID-based web endpoints would leak draft attachments to any recipient of
// the (leaked) download URL.
if unitType == unit.TypeReleases && attach.ReleaseID != 0 && !perm.CanWrite(unit.TypeReleases) {
rel, err := repo_model.GetReleaseByID(ctx, attach.ReleaseID)
if err != nil {
ctx.ServerError("GetReleaseByID", err)
return
}
if rel.IsDraft {
ctx.HTTPError(http.StatusNotFound)
return
}
}
if requiredScope, ok := attachmentReadScope(unitType); ok {
context.CheckTokenScopes(ctx, repo, requiredScope)
if ctx.Written() {

View File

@@ -27,6 +27,7 @@ import (
"gitea.dev/modules/util"
"gitea.dev/modules/web"
"gitea.dev/routers/web/feed"
shared_user "gitea.dev/routers/web/shared/user"
"gitea.dev/services/context"
"gitea.dev/services/context/upload"
"gitea.dev/services/forms"
@@ -337,6 +338,7 @@ func LatestRelease(ctx *context.Context) {
func newReleaseCommon(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
ctx.Data["PageIsReleaseList"] = true
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
@@ -344,8 +346,17 @@ func newReleaseCommon(ctx *context.Context) {
return
}
ctx.Data["Tags"] = tags
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
if err != nil {
ctx.ServerError("GetRepoAssignees", err)
return
}
ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
upload.AddUploadContext(ctx, "release")
PrepareBranchList(ctx) // for New Release page
}
@@ -414,8 +425,8 @@ func GenerateReleaseNotes(ctx *context.Context) {
// NewReleasePost response for creating a release
func NewReleasePost(ctx *context.Context) {
if ctx.HasError() {
ctx.JSONError(ctx.GetErrMsg())
newReleaseCommon(ctx)
if ctx.Written() {
return
}
@@ -439,28 +450,35 @@ func NewReleasePost(ctx *context.Context) {
// Or another choice is "always show the tag-only button" if error occurs.
ctx.Data["ShowCreateTagOnlyButton"] = form.TagOnly || rel == nil
// do some form checks
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplReleaseNew)
return
}
form.Target = util.IfZero(form.Target, ctx.Repo.Repository.DefaultBranch)
if exist, _ := git_model.IsBranchExist(ctx, ctx.Repo.Repository.ID, form.Target); !exist {
ctx.JSONError(ctx.Tr("form.target_branch_not_exist"))
ctx.RenderWithErrDeprecated(ctx.Tr("form.target_branch_not_exist"), tplReleaseNew, &form)
return
}
if !form.TagOnly && form.Title == "" {
// if not "tag only", then the title of the release cannot be empty
ctx.JSONError(ctx.Tr("repo.release.title_empty"))
ctx.RenderWithErrDeprecated(ctx.Tr("repo.release.title_empty"), tplReleaseNew, &form)
return
}
handleTagReleaseError := func(err error) {
ctx.Data["Err_TagName"] = true
switch {
case release_service.IsErrTagAlreadyExists(err):
ctx.JSONError(ctx.Tr("repo.branch.tag_collision", form.TagName))
ctx.RenderWithErrDeprecated(ctx.Tr("repo.branch.tag_collision", form.TagName), tplReleaseNew, &form)
case repo_model.IsErrReleaseAlreadyExist(err):
ctx.JSONError(ctx.Tr("repo.release.tag_name_already_exist"))
ctx.RenderWithErrDeprecated(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
case release_service.IsErrInvalidTagName(err):
ctx.JSONError(ctx.Tr("repo.release.tag_name_invalid"))
ctx.RenderWithErrDeprecated(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
case release_service.IsErrProtectedTagName(err):
ctx.JSONError(ctx.Tr("repo.release.tag_name_protected"))
ctx.RenderWithErrDeprecated(ctx.Tr("repo.release.tag_name_protected"), tplReleaseNew, &form)
default:
ctx.ServerError("handleTagReleaseError", err)
}
@@ -479,7 +497,7 @@ func NewReleasePost(ctx *context.Context) {
return
}
ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
ctx.JSONRedirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
return
}
@@ -504,7 +522,7 @@ func NewReleasePost(ctx *context.Context) {
handleTagReleaseError(err)
return
}
ctx.JSONRedirect(ctx.Repo.RepoLink + "/releases")
ctx.Redirect(ctx.Repo.RepoLink + "/releases")
return
}
@@ -512,7 +530,8 @@ func NewReleasePost(ctx *context.Context) {
// old logic: if the release is not a tag (it is a real release), do not update it on the "new release" page
// add new logic: if tag-only, do not convert the tag to a release
if form.TagOnly || !rel.IsTag {
ctx.JSONError(ctx.Tr("repo.release.tag_name_already_exist"))
ctx.Data["Err_TagName"] = true
ctx.RenderWithErrDeprecated(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
return
}
@@ -528,7 +547,7 @@ func NewReleasePost(ctx *context.Context) {
handleTagReleaseError(err)
return
}
ctx.JSONRedirect(ctx.Repo.RepoLink + "/releases")
ctx.Redirect(ctx.Repo.RepoLink + "/releases")
}
// EditRelease render release edit page
@@ -570,39 +589,55 @@ func EditRelease(ctx *context.Context) {
return
}
ctx.Data["attachments"] = rel.Attachments
// Get assignees.
assigneeUsers, err := repo_model.GetRepoAssignees(ctx, rel.Repo)
if err != nil {
ctx.ServerError("GetRepoAssignees", err)
return
}
ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
ctx.HTML(http.StatusOK, tplReleaseNew)
}
// EditReleasePost response for edit release
func EditReleasePost(ctx *context.Context) {
if ctx.HasError() {
ctx.JSONError(ctx.GetErrMsg())
form := web.GetForm(ctx).(*forms.EditReleaseForm)
newReleaseCommon(ctx)
if ctx.Written() {
return
}
form := web.GetForm(ctx).(*forms.EditReleaseForm)
ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
ctx.Data["PageIsEditRelease"] = true
tagName := ctx.PathParam("*")
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tagName)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
ctx.JSONErrorNotFound(err.Error())
ctx.NotFound(err)
} else {
ctx.ServerError("GetRelease", err)
}
return
}
if rel.IsTag {
ctx.JSONErrorNotFound() // for a pure tag release, don't allow to edit it as a release
ctx.NotFound(err) // for a pure tag release, don't allow to edit it as a release
return
}
ctx.Data["tag_name"] = rel.TagName
ctx.Data["tag_target"] = util.IfZero(rel.Target, ctx.Repo.Repository.DefaultBranch)
ctx.Data["title"] = rel.Title
ctx.Data["content"] = rel.Note
ctx.Data["prerelease"] = rel.IsPrerelease
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplReleaseNew)
return
}
const delPrefix = "attachment-del-"
const editPrefix = "attachment-edit-"
var addAttachmentUUIDs, delAttachmentUUIDs []string
@@ -624,14 +659,10 @@ func EditReleasePost(ctx *context.Context) {
rel.IsPrerelease = form.Prerelease
if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo,
rel, addAttachmentUUIDs, delAttachmentUUIDs, editAttachments); err != nil {
if upload.IsErrFileTypeForbidden(err) {
ctx.JSONError(err.Error())
} else {
ctx.ServerError("UpdateRelease", err)
}
ctx.ServerError("UpdateRelease", err)
return
}
ctx.JSONRedirect(ctx.Repo.RepoLink + "/releases")
ctx.Redirect(ctx.Repo.RepoLink + "/releases")
}
// DeleteRelease deletes a release

View File

@@ -4,14 +4,12 @@
package repo
import (
"net/http/httptest"
"testing"
"gitea.dev/models/db"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unit"
"gitea.dev/models/unittest"
"gitea.dev/modules/test"
"gitea.dev/modules/web"
"gitea.dev/services/context"
"gitea.dev/services/contexttest"
@@ -41,15 +39,15 @@ func TestNewReleasePost(t *testing.T) {
assert.NotEmpty(t, ctx.Data["ShowCreateTagOnlyButton"])
})
post := func(t *testing.T, form forms.NewReleaseForm) (*context.Context, *httptest.ResponseRecorder) {
ctx, resp := contexttest.MockContext(t, "user2/repo1/releases/new")
post := func(t *testing.T, form forms.NewReleaseForm) *context.Context {
ctx, _ := contexttest.MockContext(t, "user2/repo1/releases/new")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
contexttest.LoadGitRepo(t, ctx)
defer ctx.Repo.GitRepo.Close()
web.SetForm(ctx, &form)
NewReleasePost(ctx)
return ctx, resp
return ctx
}
loadRelease := func(t *testing.T, tagName string) *repo_model.Release {
@@ -72,7 +70,7 @@ func TestNewReleasePost(t *testing.T) {
})
t.Run("ReleaseExistsDoUpdate(non-tag)", func(t *testing.T) {
_, resp := post(t, forms.NewReleaseForm{
ctx := post(t, forms.NewReleaseForm{
TagName: "v1.1",
Target: "master",
Title: "updated-title",
@@ -82,11 +80,11 @@ func TestNewReleasePost(t *testing.T) {
require.NotNil(t, rel)
assert.False(t, rel.IsTag)
assert.Equal(t, "testing-release", rel.Title)
assert.NotEmpty(t, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
})
t.Run("ReleaseExistsDoUpdate(tag-only)", func(t *testing.T) {
ctx, resp := post(t, forms.NewReleaseForm{
ctx := post(t, forms.NewReleaseForm{
TagName: "delete-tag", // a strange name, but it is the only "is_tag=true" fixture
Target: "master",
Title: "updated-title",
@@ -97,12 +95,12 @@ func TestNewReleasePost(t *testing.T) {
require.NotNil(t, rel)
assert.True(t, rel.IsTag) // the record should not be updated because the request is "tag-only". TODO: need to improve the logic?
assert.Equal(t, "delete-tag", rel.Title)
assert.NotEmpty(t, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
assert.NotEmpty(t, ctx.Data["ShowCreateTagOnlyButton"]) // still show the "tag-only" button
})
t.Run("ReleaseExistsDoUpdate(tag-release)", func(t *testing.T) {
ctx, _ := post(t, forms.NewReleaseForm{
ctx := post(t, forms.NewReleaseForm{
TagName: "delete-tag", // a strange name, but it is the only "is_tag=true" fixture
Target: "master",
Title: "updated-title",
@@ -116,7 +114,7 @@ func TestNewReleasePost(t *testing.T) {
})
t.Run("TagOnly", func(t *testing.T) {
ctx, _ := post(t, forms.NewReleaseForm{
ctx := post(t, forms.NewReleaseForm{
TagName: "new-tag-only",
Target: "master",
Title: "title",
@@ -130,7 +128,7 @@ func TestNewReleasePost(t *testing.T) {
})
t.Run("TagOnlyConflict", func(t *testing.T) {
_, resp := post(t, forms.NewReleaseForm{
ctx := post(t, forms.NewReleaseForm{
TagName: "v1.1",
Target: "master",
Title: "title",
@@ -140,7 +138,7 @@ func TestNewReleasePost(t *testing.T) {
rel := loadRelease(t, "v1.1")
require.NotNil(t, rel)
assert.False(t, rel.IsTag)
assert.NotEmpty(t, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
})
}

View File

@@ -429,7 +429,7 @@ func telegramHookParams(ctx *context.Context) webhookParams {
return webhookParams{
Type: webhook_module.TELEGRAM,
URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendRichMessage?chat_id=%s&message_thread_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID), url.QueryEscape(form.ThreadID)),
URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&message_thread_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID), url.QueryEscape(form.ThreadID)),
ContentType: webhook.ContentTypeJSON,
WebhookForm: form.WebhookForm,
Meta: &webhook_service.TelegramMeta{

View File

@@ -14,10 +14,8 @@ import (
repo_model "gitea.dev/models/repo"
user_model "gitea.dev/models/user"
actions_module "gitea.dev/modules/actions"
"gitea.dev/modules/actions/jobparser"
"gitea.dev/modules/commitstatus"
"gitea.dev/modules/log"
api "gitea.dev/modules/structs"
"gitea.dev/modules/util"
webhook_module "gitea.dev/modules/webhook"
commitstatus_service "gitea.dev/services/repository/commitstatus"
@@ -149,78 +147,21 @@ func createCommitStatus(ctx context.Context, repo *repo_model.Repository, event,
// scopedPrefix is computed once per run by the caller. The settings page derives the same string to preview expected checks.
ctxName = actions_module.ScopedWorkflowStatusContextName(scopedPrefix, displayName, job.Name, event)
}
targetURL := fmt.Sprintf("%s/jobs/%d", run.Link(), job.ID)
return createWorkflowCommitStatus(ctx, repo, commitID, ctxName, run.WorkflowID, toCommitStatus(job.Status), targetURL, toCommitStatusDescription(job))
}
// CreateSkippedCommitStatusForFilteredWorkflow posts a skipped commit status for each job of a
// workflow that matched the triggering event but was excluded by a branch/paths filter.
// This lets a required status check tied to that context be satisfied without the workflow running.
// No ActionRun is created, so the status has no target URL (there is no run/job to link to).
// A non-empty scopedPrefix prefixes each context with its source repo, matching scoped runs.
func CreateSkippedCommitStatusForFilteredWorkflow(ctx context.Context, repo *repo_model.Repository, event webhook_module.HookEventType, triggerEvent, workflowID string, content []byte, payload api.Payloader, scopedPrefix string) error {
// Derive the status event name and target commit from the payload.
// TODO: this mirrors getCommitStatusEventNameAndCommitID, which derives the same from a persisted run. Should merge the logic if possible.
var statusEvent, commitID string
switch event {
case webhook_module.HookEventPush:
if p, ok := payload.(*api.PushPayload); ok && p.HeadCommit != nil {
statusEvent, commitID = "push", p.HeadCommit.ID
}
case webhook_module.HookEventPullRequest,
webhook_module.HookEventPullRequestSync,
webhook_module.HookEventPullRequestAssign,
webhook_module.HookEventPullRequestLabel,
webhook_module.HookEventPullRequestReviewRequest,
webhook_module.HookEventPullRequestMilestone:
if p, ok := payload.(*api.PullRequestPayload); ok && p.PullRequest != nil && p.PullRequest.Head != nil {
statusEvent, commitID = "pull_request", p.PullRequest.Head.Sha
if triggerEvent == actions_module.GithubEventPullRequestTarget {
statusEvent = "pull_request_target"
}
}
}
if statusEvent == "" || commitID == "" {
return nil // unsupported event or missing commit id, nothing to post
}
workflows, err := jobparser.Parse(content)
if err != nil {
return fmt.Errorf("jobparser.Parse: %w", err)
}
displayName := actions_module.WorkflowDisplayName(workflowID, content)
for _, sw := range workflows {
_, job := sw.Job()
if job == nil {
continue
}
jobName := util.EllipsisDisplayString(job.Name, 255) // run creation truncates job names the same way
ctxName := actions_module.WorkflowStatusContextName(displayName, jobName, statusEvent)
if scopedPrefix != "" {
ctxName = actions_module.ScopedWorkflowStatusContextName(scopedPrefix, displayName, jobName, statusEvent)
}
// "Skipped" mirrors toCommitStatusDescription for StatusSkipped.
if err := createWorkflowCommitStatus(ctx, repo, commitID, ctxName, workflowID, commitstatus.CommitStatusSkipped, "", "Skipped"); err != nil {
return err
}
}
return nil
}
// createWorkflowCommitStatus posts the commit status for one workflow-job context.
func createWorkflowCommitStatus(ctx context.Context, repo *repo_model.Repository, commitID, ctxName, workflowID string, state commitstatus.CommitStatusState, targetURL, description string) error {
// Mix the workflow file path into the hash so two workflow files that
// share the same `name:` and job name produce distinct commit statuses
// even though they render identically — matching GitHub's behavior
// (issue #35699).
ctxHash := git_model.HashCommitStatusContext(ctxName + "\x00" + workflowID)
ctxHash := git_model.HashCommitStatusContext(ctxName + "\x00" + run.WorkflowID)
// Pre-fix rows were hashed from Context alone. If a pre-existing row with
// the legacy hash is still the "latest" for this SHA, reuse that hash so
// the new row supersedes it; otherwise the old pending status would stay
// stuck forever (it lives in its own dedupe group). Only relevant for
// in-flight workflows at upgrade time.
legacyHash := git_model.HashCommitStatusContext(ctxName)
state := toCommitStatus(job.Status)
targetURL := fmt.Sprintf("%s/jobs/%d", run.Link(), job.ID)
description := toCommitStatusDescription(job)
statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commitID, db.ListOptionsAll)
if err != nil {

View File

@@ -183,9 +183,8 @@ func notify(ctx context.Context, input *notifyInput) error {
}
var detectedWorkflows []*actions_module.DetectedWorkflow
var filteredWorkflows []*actions_module.DetectedWorkflow
actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig()
workflows, schedules, filtered, err := actions_module.DetectWorkflows(gitRepo, commit,
workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit,
input.Event,
input.Payload,
shouldDetectSchedules,
@@ -213,17 +212,6 @@ func notify(ctx context.Context, input *notifyInput) error {
}
}
for _, wf := range filtered {
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
log.Trace("repo %s has disable workflows %s", input.Repo.RelativePath(), wf.EntryName)
continue
}
if wf.TriggerEvent.Name != actions_module.GithubEventPullRequestTarget {
filteredWorkflows = append(filteredWorkflows, wf)
}
}
if input.PullRequest != nil {
// detect pull_request_target workflows
baseRef := git.BranchPrefix + input.PullRequest.BaseBranch
@@ -231,7 +219,7 @@ func notify(ctx context.Context, input *notifyInput) error {
if err != nil {
return fmt.Errorf("gitRepo.GetCommit: %w", err)
}
baseWorkflows, _, baseFiltered, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload, false)
baseWorkflows, _, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload, false)
if err != nil {
return fmt.Errorf("DetectWorkflows: %w", err)
}
@@ -239,24 +227,11 @@ func notify(ctx context.Context, input *notifyInput) error {
log.Trace("repo %s with commit %s couldn't find pull_request_target workflows", input.Repo.RelativePath(), baseCommit.ID)
} else {
for _, wf := range baseWorkflows {
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
log.Trace("repo %s has disable workflows %s", input.Repo.RelativePath(), wf.EntryName)
continue
}
if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget {
detectedWorkflows = append(detectedWorkflows, wf)
}
}
}
for _, wf := range baseFiltered {
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
log.Trace("repo %s has disable workflows %s", input.Repo.RelativePath(), wf.EntryName)
continue
}
if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget {
filteredWorkflows = append(filteredWorkflows, wf)
}
}
}
if shouldDetectSchedules {
@@ -269,8 +244,6 @@ func notify(ctx context.Context, input *notifyInput) error {
return err
}
handleFilteredWorkflows(ctx, input, filteredWorkflows)
return detectAndHandleScopedWorkflows(ctx, input, ref, gitRepo, commit)
}
@@ -396,16 +369,6 @@ func buildApproveAndInsertRun(
return nil
}
// handleFilteredWorkflows posts a skipped commit status for each workflow that matched the event but was excluded by a branch/paths filter.
func handleFilteredWorkflows(ctx context.Context, input *notifyInput, filteredWorkflows []*actions_module.DetectedWorkflow) {
for _, dwf := range filteredWorkflows {
if err := CreateSkippedCommitStatusForFilteredWorkflow(ctx, input.Repo, input.Event, dwf.TriggerEvent.Name, dwf.EntryName, dwf.Content, input.Payload, ""); err != nil {
log.Error("repo %s: skipped commit status for workflow %s: %v", input.Repo.RelativePath(), dwf.EntryName, err)
continue
}
}
}
func newNotifyInputFromIssue(issue *issues_model.Issue, event webhook_module.HookEventType) *notifyInput {
return newNotifyInput(issue.Repo, issue.Poster, event)
}
@@ -677,7 +640,7 @@ func detectAndHandleScopedWorkflows(
continue
}
sourceCommitSHA, detected, filtered, err := detectScopedWorkflowsForSource(ctx, input, consumerGitRepo, consumerCommit, sourceRepo)
sourceCommitSHA, detected, err := detectScopedWorkflowsForSource(ctx, input, consumerGitRepo, consumerCommit, sourceRepo)
if err != nil {
log.Error("scoped workflows: source %d for consumer %s: %v", sourceRepoID, input.Repo.RelativePath(), err)
continue
@@ -695,40 +658,23 @@ func detectAndHandleScopedWorkflows(
continue
}
}
// A filtered-out scoped workflow posts a skipped commit status.
if len(filtered) > 0 {
scopedPrefix := actions_model.ScopedStatusContextPrefix(ctx, sourceRepo.ID)
for _, dwf := range filtered {
if actions_model.ScopedWorkflowOptedOut(actionsConfig, sources, sourceRepo.ID, dwf.EntryName) {
continue
}
if err := CreateSkippedCommitStatusForFilteredWorkflow(ctx, input.Repo, input.Event, dwf.TriggerEvent.Name, dwf.EntryName, dwf.Content, input.Payload, scopedPrefix); err != nil {
log.Error("scoped workflows: skipped commit status for source %s workflow %s: %v", sourceRepo.RelativePath(), dwf.EntryName, err)
continue
}
}
}
}
return nil
}
// detectScopedWorkflowsForSource detects the scoped workflows from the source repo at its default branch.
// detected are the workflows to run; filtered matched the event but were excluded by a branch/paths
// filter and post a skipped commit status.
// detectScopedWorkflowsForSource detects the scoped workflows from the source repo at its default branch
func detectScopedWorkflowsForSource(
ctx context.Context,
input *notifyInput,
consumerGitRepo *git.Repository,
consumerCommit *git.Commit,
sourceRepo *repo_model.Repository,
) (sourceCommitSHA string, detected, filtered []*actions_module.DetectedWorkflow, err error) {
) (sourceCommitSHA string, detected []*actions_module.DetectedWorkflow, err error) {
// scoped workflow content is always taken from the source repo's default branch; the parse is cached per (source, default-branch SHA) and reused across consuming repos/events
sourceCommitSHA, parsed, err := LoadParsedScopedWorkflows(ctx, sourceRepo)
if err != nil {
return "", nil, nil, err
return "", nil, err
}
detected, filtered = actions_module.MatchScopedWorkflows(parsed, consumerGitRepo, consumerCommit, input.Event, input.Payload)
return sourceCommitSHA, detected, filtered, nil
return sourceCommitSHA, actions_module.MatchScopedWorkflows(parsed, consumerGitRepo, consumerCommit, input.Event, input.Payload), nil
}

View File

@@ -76,15 +76,6 @@ func PickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv
NotifyWorkflowRunStatusUpdateWithReload(ctx, job.RepoID, job.RunID)
}
// The job is claimed and its payload assembled, but if the request context was cancelled meanwhile, response can no longer reach the runner.
// Release the claim so another runner can pick the job up.
if err := ctx.Err(); err != nil {
if relErr := actions_model.ReleaseTaskForRunner(ctx, t); relErr != nil {
log.Error("ReleaseTaskForRunner [task_id: %d]: %v", t.ID, relErr)
}
return nil, false, err
}
return task, true, nil
}

View File

@@ -104,6 +104,10 @@ type APINotFound struct{}
// swagger:response conflict
type APIConflict struct{}
// APIRedirect is a redirect response
// swagger:response redirect
type APIRedirect struct{}
// APIString is a string response
// swagger:response string
type APIString string

View File

@@ -20,11 +20,9 @@ import (
"gitea.dev/modules/graceful"
"gitea.dev/modules/log"
"gitea.dev/modules/repository"
"gitea.dev/modules/setting"
"gitea.dev/modules/storage"
"gitea.dev/modules/timeutil"
"gitea.dev/modules/util"
"gitea.dev/services/context/upload"
notify_service "gitea.dev/services/notify"
)
@@ -321,17 +319,13 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
}
for uuid, newName := range editAttachments {
if deletedUUIDs.Contains(uuid) {
continue
}
if err = upload.Verify(nil, newName, setting.Repository.Release.AllowedTypes); err != nil {
return err
}
if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{
UUID: uuid,
Name: newName,
}, "name"); err != nil {
return err
if !deletedUUIDs.Contains(uuid) {
if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{
UUID: uuid,
Name: newName,
}, "name"); err != nil {
return err
}
}
}
}

View File

@@ -12,11 +12,8 @@ import (
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/gitrepo"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
"gitea.dev/modules/timeutil"
"gitea.dev/services/attachment"
"gitea.dev/services/context/upload"
_ "gitea.dev/models/actions"
@@ -273,17 +270,6 @@ func TestRelease_Update(t *testing.T) {
assert.Equal(t, release.ID, release.Attachments[0].ReleaseID)
assert.Equal(t, "test2.txt", release.Attachments[0].Name)
defer test.MockVariableValue(&setting.Repository.Release.AllowedTypes, ".zip")()
err = UpdateRelease(t.Context(), user, gitRepo, release, nil, nil, map[string]string{
attach.UUID: "test.exe",
})
assert.Error(t, err)
assert.True(t, upload.IsErrFileTypeForbidden(err))
release.Attachments = nil
assert.NoError(t, repo_model.GetReleaseAttachments(t.Context(), release))
assert.Len(t, release.Attachments, 1)
assert.Equal(t, "test2.txt", release.Attachments[0].Name)
// delete the attachment
assert.NoError(t, UpdateRelease(t.Context(), user, gitRepo, release, nil, []string{attach.UUID}, nil))
release.Attachments = nil

View File

@@ -42,38 +42,6 @@ type ArchiveRequest struct {
archiveRefShortName string // the ref short name to download the archive, for example: "master", "v1.0.0", "commit id"
}
type archiveQueueItem struct {
RepoID int64 `json:"RepoID"`
Type repo_model.ArchiveType `json:"Type"`
CommitID string `json:"CommitID"`
Paths []string `json:"Paths,omitempty"`
ArchiveRefShortName string `json:"ArchiveRefShortName,omitempty"`
}
func (aReq *ArchiveRequest) toQueueItem() *archiveQueueItem {
return &archiveQueueItem{
RepoID: aReq.Repo.ID,
Type: aReq.Type,
CommitID: aReq.CommitID,
Paths: aReq.Paths,
ArchiveRefShortName: aReq.archiveRefShortName,
}
}
func (item *archiveQueueItem) toArchiveRequest(ctx context.Context) (*ArchiveRequest, error) {
repo, err := repo_model.GetRepositoryByID(ctx, item.RepoID)
if err != nil {
return nil, err
}
return &ArchiveRequest{
Repo: repo,
Type: item.Type,
CommitID: item.CommitID,
Paths: item.Paths,
archiveRefShortName: item.ArchiveRefShortName,
}, nil
}
// NewRequest creates an archival request, based on the URI. The
// resulting ArchiveRequest is suitable for being passed to Await()
// if it's determined that the request still needs to be satisfied.
@@ -259,18 +227,13 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver
return archiver, nil
}
var archiverQueue *queue.WorkerPoolQueue[*archiveQueueItem]
var archiverQueue *queue.WorkerPoolQueue[*ArchiveRequest]
// Init initializes archiver
func Init(ctx context.Context) error {
handler := func(items ...*archiveQueueItem) []*archiveQueueItem {
for _, item := range items {
log.Trace("ArchiverData Process: %#v", item)
archiveReq, err := item.toArchiveRequest(ctx)
if err != nil {
log.Error("Archive repo %d: %v", item.RepoID, err)
continue
}
handler := func(items ...*ArchiveRequest) []*ArchiveRequest {
for _, archiveReq := range items {
log.Trace("ArchiverData Process: %#v", archiveReq)
if archiver, err := doArchive(ctx, archiveReq); err != nil {
log.Error("Archive %v failed: %v", archiveReq, err)
} else {
@@ -291,15 +254,14 @@ func Init(ctx context.Context) error {
// StartArchive push the archive request to the queue
func StartArchive(request *ArchiveRequest) error {
item := request.toQueueItem()
has, err := archiverQueue.Has(item)
has, err := archiverQueue.Has(request)
if err != nil {
return err
}
if has {
return nil
}
return archiverQueue.Push(item)
return archiverQueue.Push(request)
}
func deleteOldRepoArchiver(ctx context.Context, archiver *repo_model.RepoArchiver) error {

View File

@@ -7,9 +7,7 @@ import (
"testing"
"time"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest"
"gitea.dev/modules/json"
"gitea.dev/modules/util"
"gitea.dev/services/contexttest"
@@ -23,22 +21,6 @@ func TestMain(m *testing.M) {
unittest.MainTest(m)
}
func TestArchiveQueueItemJSON(t *testing.T) {
orig := &archiveQueueItem{
RepoID: 7,
Type: repo_model.ArchiveZip,
CommitID: "abc123",
Paths: []string{"agents"},
ArchiveRefShortName: "main",
}
bs, err := json.Marshal(orig)
require.NoError(t, err)
var decoded archiveQueueItem
require.NoError(t, json.Unmarshal(bs, &decoded))
assert.Equal(t, *orig, decoded)
}
func TestArchive_Basic(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

View File

@@ -8,7 +8,6 @@ import (
"fmt"
"html"
"net/http"
"net/url"
"strings"
webhook_model "gitea.dev/models/webhook"
@@ -24,12 +23,9 @@ import (
type (
// TelegramPayload represents
TelegramPayload struct {
RichMessage InputRichMessage `json:"rich_message"`
}
// InputRichMessage represents input rich message
InputRichMessage struct {
HTML string `json:"html"`
Message string `json:"text"`
ParseMode string `json:"parse_mode"`
DisableWebPreview bool `json:"disable_web_page_preview"`
}
// TelegramMeta contains the telegram metadata
@@ -199,21 +195,13 @@ func (telegramConvertor) WorkflowJob(p *api.WorkflowJobPayload) (TelegramPayload
func createTelegramPayloadHTML(msgHTML string) TelegramPayload {
// https://core.telegram.org/bots/api#formatting-options
return TelegramPayload{
RichMessage: InputRichMessage{
HTML: strings.TrimSpace(string(markup.Sanitize(msgHTML))),
},
Message: strings.TrimSpace(string(markup.Sanitize(msgHTML))),
ParseMode: "HTML",
DisableWebPreview: true,
}
}
func newTelegramRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
u, err := url.Parse(w.URL)
if err != nil {
return nil, nil, err
}
if urlPrefix, ok := strings.CutSuffix(u.Path, "/sendMessage"); ok {
u.Path = urlPrefix + "/sendRichMessage"
w.URL = u.String()
}
var pc payloadConvertor[TelegramPayload] = telegramConvertor{}
return newJSONRequest(pc, w, t, true)
}

View File

@@ -21,9 +21,9 @@ func TestTelegramPayload(t *testing.T) {
t.Run("Correct webhook params", func(t *testing.T) {
p := createTelegramPayloadHTML(`<a href=".">testMsg</a> <bad>`)
assert.Equal(t, TelegramPayload{
RichMessage: InputRichMessage{
HTML: `<a href="." rel="nofollow">testMsg</a>`,
},
Message: `<a href="." rel="nofollow">testMsg</a>`,
ParseMode: "HTML",
DisableWebPreview: true,
}, p)
})
@@ -33,7 +33,7 @@ func TestTelegramPayload(t *testing.T) {
pl, err := tc.Create(p)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] branch <a href="http://localhost:3000/test/repo/src/test" rel="nofollow">test</a> created`, pl.RichMessage.HTML)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] branch <a href="http://localhost:3000/test/repo/src/test" rel="nofollow">test</a> created`, pl.Message)
})
t.Run("Delete", func(t *testing.T) {
@@ -42,7 +42,7 @@ func TestTelegramPayload(t *testing.T) {
pl, err := tc.Delete(p)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] branch <a href="http://localhost:3000/test/repo/src/test" rel="nofollow">test</a> deleted`, pl.RichMessage.HTML)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] branch <a href="http://localhost:3000/test/repo/src/test" rel="nofollow">test</a> deleted`, pl.Message)
})
t.Run("Fork", func(t *testing.T) {
@@ -51,7 +51,7 @@ func TestTelegramPayload(t *testing.T) {
pl, err := tc.Fork(p)
require.NoError(t, err)
assert.Equal(t, `test/repo2 is forked to <a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>`, pl.RichMessage.HTML)
assert.Equal(t, `test/repo2 is forked to <a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>`, pl.Message)
})
t.Run("Push", func(t *testing.T) {
@@ -62,7 +62,7 @@ func TestTelegramPayload(t *testing.T) {
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>:<a href="http://localhost:3000/test/repo/src/test" rel="nofollow">test</a>] 2 new commits
[<a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778" rel="nofollow">2020558</a>] commit message - user1
[<a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778" rel="nofollow">2020558</a>] commit message - user1`, pl.RichMessage.HTML)
[<a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778" rel="nofollow">2020558</a>] commit message - user1`, pl.Message)
})
t.Run("Issue", func(t *testing.T) {
@@ -74,13 +74,13 @@ func TestTelegramPayload(t *testing.T) {
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Issue opened: <a href="http://localhost:3000/test/repo/issues/2" rel="nofollow">#2 crash</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>
issue body`, pl.RichMessage.HTML)
issue body`, pl.Message)
p.Action = api.HookIssueClosed
pl, err = tc.Issue(p)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Issue closed: <a href="http://localhost:3000/test/repo/issues/2" rel="nofollow">#2 crash</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.RichMessage.HTML)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Issue closed: <a href="http://localhost:3000/test/repo/issues/2" rel="nofollow">#2 crash</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.Message)
})
t.Run("IssueComment", func(t *testing.T) {
@@ -90,7 +90,7 @@ issue body`, pl.RichMessage.HTML)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] New comment on issue <a href="http://localhost:3000/test/repo/issues/2" rel="nofollow">#2 crash</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>
more info needed`, pl.RichMessage.HTML)
more info needed`, pl.Message)
})
t.Run("PullRequest", func(t *testing.T) {
@@ -100,7 +100,7 @@ more info needed`, pl.RichMessage.HTML)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Pull request opened: <a href="http://localhost:3000/test/repo/pulls/12" rel="nofollow">#12 Fix bug</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>
fixes bug #2`, pl.RichMessage.HTML)
fixes bug #2`, pl.Message)
})
t.Run("PullRequestComment", func(t *testing.T) {
@@ -110,7 +110,7 @@ fixes bug #2`, pl.RichMessage.HTML)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] New comment on pull request <a href="http://localhost:3000/test/repo/pulls/12" rel="nofollow">#12 Fix bug</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>
changes requested`, pl.RichMessage.HTML)
changes requested`, pl.Message)
})
t.Run("Review", func(t *testing.T) {
@@ -121,7 +121,7 @@ changes requested`, pl.RichMessage.HTML)
require.NoError(t, err)
assert.Equal(t, `[test/repo] Pull request review approved: #12 Fix bug
good job`, pl.RichMessage.HTML)
good job`, pl.Message)
})
t.Run("Repository", func(t *testing.T) {
@@ -130,7 +130,7 @@ good job`, pl.RichMessage.HTML)
pl, err := tc.Repository(p)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Repository created`, pl.RichMessage.HTML)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Repository created`, pl.Message)
})
t.Run("Package", func(t *testing.T) {
@@ -139,7 +139,7 @@ good job`, pl.RichMessage.HTML)
pl, err := tc.Package(p)
require.NoError(t, err)
assert.Equal(t, `Package created: <a href="http://localhost:3000/user1/-/packages/container/GiteaContainer/latest" rel="nofollow">GiteaContainer:latest</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.RichMessage.HTML)
assert.Equal(t, `Package created: <a href="http://localhost:3000/user1/-/packages/container/GiteaContainer/latest" rel="nofollow">GiteaContainer:latest</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.Message)
})
t.Run("Wiki", func(t *testing.T) {
@@ -149,19 +149,19 @@ good job`, pl.RichMessage.HTML)
pl, err := tc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] New wiki page &#39;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#39; (Wiki change comment) by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.RichMessage.HTML)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] New wiki page &#39;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#39; (Wiki change comment) by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.Message)
p.Action = api.HookWikiEdited
pl, err = tc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Wiki page &#39;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#39; edited (Wiki change comment) by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.RichMessage.HTML)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Wiki page &#39;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#39; edited (Wiki change comment) by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.Message)
p.Action = api.HookWikiDeleted
pl, err = tc.Wiki(p)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Wiki page &#39;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#39; deleted by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.RichMessage.HTML)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Wiki page &#39;<a href="http://localhost:3000/test/repo/wiki/index" rel="nofollow">index</a>&#39; deleted by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.Message)
})
t.Run("Release", func(t *testing.T) {
@@ -170,7 +170,7 @@ good job`, pl.RichMessage.HTML)
pl, err := tc.Release(p)
require.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Release created: <a href="http://localhost:3000/test/repo/releases/tag/v1.0" rel="nofollow">v1.0</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.RichMessage.HTML)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>] Release created: <a href="http://localhost:3000/test/repo/releases/tag/v1.0" rel="nofollow">v1.0</a> by <a href="https://try.gitea.io/user1" rel="nofollow">user1</a>`, pl.Message)
})
}
@@ -183,7 +183,7 @@ func TestTelegramJSONPayload(t *testing.T) {
RepoID: 3,
IsActive: true,
Type: webhook_module.TELEGRAM,
URL: "https://telegram.example.com/sendMessage",
URL: "https://telegram.example.com/",
Meta: ``,
HTTPMethod: "POST",
}
@@ -200,7 +200,7 @@ func TestTelegramJSONPayload(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://telegram.example.com/sendRichMessage", req.URL.String())
assert.Equal(t, "https://telegram.example.com/", req.URL.String())
assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body TelegramPayload
@@ -208,5 +208,5 @@ func TestTelegramJSONPayload(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, `[<a href="http://localhost:3000/test/repo" rel="nofollow">test/repo</a>:<a href="http://localhost:3000/test/repo/src/test" rel="nofollow">test</a>] 2 new commits
[<a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778" rel="nofollow">2020558</a>] commit message - user1
[<a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778" rel="nofollow">2020558</a>] commit message - user1`, body.RichMessage.HTML)
[<a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778" rel="nofollow">2020558</a>] commit message - user1`, body.Message)
}

View File

@@ -12,6 +12,9 @@ base: core24
adopt-info: gitea
platforms:
armhf:
build-on: [armhf]
build-for: [armhf]
amd64:
build-on: [amd64]
build-for: [amd64]

View File

@@ -105,7 +105,7 @@
<strong class="tw-text-red">{{ctx.Locale.TrN .Activity.Code.Deletions "repo.activity.git_stats_deletion_1" "repo.activity.git_stats_deletion_n" .Activity.Code.Deletions}}</strong>.
</div>
<div class="ui attached segment">
<div id="repo-activity-top-authors-chart" class="tw-h-[100px]"></div>
<div id="repo-activity-top-authors-chart"></div>
</div>
</div>
{{end}}

View File

@@ -13,7 +13,7 @@
</h2>
{{template "base/alert" .}}
<form class="ui form form-fetch-action" action="{{.Link}}" method="post" data-global-init="initReleaseEditForm"
<form class="ui form" action="{{.Link}}" method="post" data-global-init="initReleaseEditForm"
data-existing-tags="{{JsonUtils.EncodeToString .Tags}}"
data-tag-helper="{{ctx.Locale.Tr "repo.release.tag_helper"}}"
data-tag-helper-new="{{ctx.Locale.Tr "repo.release.tag_helper_new"}}"

View File

@@ -22762,7 +22762,7 @@
"type": "object",
"properties": {
"permission": {
"description": "Permission level to grant the collaborator",
"description": "Permission level to grant the collaborator\nread RepoWritePermissionRead\nwrite RepoWritePermissionWrite\nadmin RepoWritePermissionAdmin",
"type": "string",
"enum": [
"read",
@@ -22917,7 +22917,6 @@
"x-go-name": "DownloadURL"
},
"created_at": {
"description": "Created is the time when the attachment was uploaded",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -23066,8 +23065,8 @@
"x-go-name": "BlockOnRejectedReviews"
},
"branch_name": {
"description": "Deprecated: true",
"type": "string",
"x-deprecated": true,
"x-go-name": "BranchName"
},
"bypass_allowlist_teams": {
@@ -23390,7 +23389,7 @@
"x-go-name": "SHA"
},
"state": {
"description": "State is the overall combined status state",
"description": "State is the overall combined status state\npending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped is for when CommitStatus is Skipped",
"type": "string",
"enum": [
"pending",
@@ -23504,7 +23503,6 @@
"$ref": "#/definitions/User"
},
"created": {
"description": "Created is the time when the commit was created",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -23568,13 +23566,11 @@
"type": "object",
"properties": {
"author": {
"description": "Author is the author date for the commit",
"type": "string",
"format": "date-time",
"x-go-name": "Author"
},
"committer": {
"description": "Committer is the committer date for the commit",
"type": "string",
"format": "date-time",
"x-go-name": "Committer"
@@ -23587,7 +23583,6 @@
"title": "CommitMeta contains meta information of a commit in terms of API.",
"properties": {
"created": {
"description": "Created is the time when the commit was created",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -23659,7 +23654,7 @@
"x-go-name": "ID"
},
"status": {
"description": "State represents the status state (pending, success, error, failure)",
"description": "State represents the status state (pending, success, error, failure)\npending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped is for when CommitStatus is Skipped",
"type": "string",
"enum": [
"pending",
@@ -23700,7 +23695,6 @@
"x-go-name": "Date"
},
"email": {
"description": "Email is the person's email address",
"type": "string",
"format": "email",
"x-go-name": "Email"
@@ -23945,8 +23939,8 @@
"x-go-name": "BlockOnRejectedReviews"
},
"branch_name": {
"description": "Deprecated: true",
"type": "string",
"x-deprecated": true,
"x-go-name": "BranchName"
},
"bypass_allowlist_teams": {
@@ -24103,10 +24097,9 @@
"x-go-name": "BranchName"
},
"old_branch_name": {
"description": "Name of the old branch to create from",
"description": "Deprecated: true\nName of the old branch to create from",
"type": "string",
"uniqueItems": true,
"x-deprecated": true,
"x-go-name": "OldBranchName"
},
"old_ref_name": {
@@ -24229,7 +24222,6 @@
],
"properties": {
"active": {
"description": "Whether the webhook should be active upon creation",
"type": "boolean",
"default": false,
"x-go-name": "Active"
@@ -24261,7 +24253,6 @@
"x-go-name": "Name"
},
"type": {
"description": "The type of the webhook to create",
"type": "string",
"enum": [
"dingtalk",
@@ -24296,7 +24287,6 @@
],
"properties": {
"body": {
"description": "Body is the comment text content",
"type": "string",
"x-go-name": "Body"
}
@@ -24427,7 +24417,6 @@
"example": false
},
"name": {
"description": "Name is the display name for the new label",
"type": "string",
"x-go-name": "Name"
}
@@ -24444,13 +24433,11 @@
"x-go-name": "Description"
},
"due_on": {
"description": "Deadline is the due date for the milestone",
"type": "string",
"format": "date-time",
"x-go-name": "Deadline"
},
"state": {
"description": "State indicates the initial state of the milestone",
"type": "string",
"enum": [
"open",
@@ -24554,7 +24541,7 @@
"x-go-name": "UserName"
},
"visibility": {
"description": "possible values are `public` (default), `limited` or `private`",
"description": "possible values are `public` (default), `limited` or `private`\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate",
"type": "string",
"enum": [
"public",
@@ -24846,7 +24833,7 @@
"x-go-name": "Name"
},
"object_format_name": {
"description": "ObjectFormatName of the underlying git repository, empty string for default (sha1)",
"description": "ObjectFormatName of the underlying git repository, empty string for default (sha1)\nsha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256",
"type": "string",
"enum": [
"sha1",
@@ -24899,7 +24886,7 @@
"x-go-name": "Description"
},
"state": {
"description": "State represents the status state to set (pending, success, error, failure)",
"description": "State represents the status state to set (pending, success, error, failure)\npending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped is for when CommitStatus is Skipped",
"type": "string",
"enum": [
"pending",
@@ -24933,7 +24920,6 @@
"x-go-name": "Message"
},
"tag_name": {
"description": "The name of the tag to create",
"type": "string",
"x-go-name": "TagName"
},
@@ -25010,20 +24996,18 @@
"x-go-name": "Permission"
},
"units": {
"description": "Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.",
"type": "array",
"items": {
"type": "string"
},
"x-deprecated": true,
"x-go-name": "Units",
"example": [
"repo.actions",
"repo.packages",
"repo.code",
"repo.issues",
"repo.ext_issues",
"repo.wiki",
"repo.ext_wiki",
"repo.pulls",
"repo.releases",
"repo.projects",
@@ -25036,21 +25020,10 @@
"type": "string"
},
"x-go-name": "UnitsMap",
"example": {
"repo.actions": "read",
"repo.code": "read",
"repo.ext_issues": "none",
"repo.ext_wiki": "none",
"repo.issues": "write",
"repo.packages": "read",
"repo.projects": "none",
"repo.pulls": "owner",
"repo.releases": "none",
"repo.wiki": "admin"
}
"example": "{\"repo.actions\",\"repo.packages\",\"repo.code\":\"read\",\"repo.issues\":\"write\",\"repo.ext_issues\":\"none\",\"repo.wiki\":\"admin\",\"repo.pulls\":\"owner\",\"repo.releases\":\"none\",\"repo.projects\":\"none\",\"repo.ext_wiki\":\"none\"}"
},
"visibility": {
"description": "Team visibility within the organization. Defaults to \"private\".",
"description": "Team visibility within the organization. Defaults to \"private\".\npublic TeamVisibilityPublic\nlimited TeamVisibilityLimited\nprivate TeamVisibilityPrivate",
"type": "string",
"enum": [
"public",
@@ -25079,6 +25052,7 @@
},
"email": {
"type": "string",
"format": "email",
"x-go-name": "Email"
},
"full_name": {
@@ -25124,7 +25098,7 @@
"x-go-name": "Username"
},
"visibility": {
"description": "User visibility level: public, limited, or private",
"description": "User visibility level: public, limited, or private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate",
"type": "string",
"enum": [
"public",
@@ -25324,7 +25298,6 @@
"type": "object",
"properties": {
"created_at": {
"description": "Created is the time when the deploy key was added",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -25661,7 +25634,6 @@
],
"properties": {
"body": {
"description": "Body is the updated comment text content",
"type": "string",
"x-go-name": "Body"
}
@@ -25826,7 +25798,7 @@
"x-go-name": "RepoAdminChangeTeamAccess"
},
"visibility": {
"description": "possible values are `public`, `limited` or `private`",
"description": "possible values are `public`, `limited` or `private`\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate",
"type": "string",
"enum": [
"public",
@@ -26226,12 +26198,10 @@
"x-go-name": "Permission"
},
"units": {
"description": "Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.",
"type": "array",
"items": {
"type": "string"
},
"x-deprecated": true,
"x-go-name": "Units",
"example": [
"repo.code",
@@ -26262,7 +26232,7 @@
}
},
"visibility": {
"description": "Team visibility within the organization. When omitted, visibility is\nleft unchanged.",
"description": "Team visibility within the organization. When omitted, visibility is\nleft unchanged.\npublic TeamVisibilityPublic\nlimited TeamVisibilityLimited\nprivate TeamVisibilityPrivate",
"type": "string",
"enum": [
"public",
@@ -26314,7 +26284,6 @@
"x-go-name": "Description"
},
"email": {
"description": "The email address of the user",
"type": "string",
"format": "email",
"x-go-name": "Email"
@@ -26362,13 +26331,12 @@
"x-go-name": "Restricted"
},
"source_id": {
"description": "The authentication source ID to associate with the user",
"type": "integer",
"format": "int64",
"x-go-name": "SourceID"
},
"visibility": {
"description": "User visibility level: public, limited, or private",
"description": "User visibility level: public, limited, or private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate",
"type": "string",
"enum": [
"public",
@@ -26391,7 +26359,6 @@
"type": "object",
"properties": {
"email": {
"description": "The email address",
"type": "string",
"format": "email",
"x-go-name": "Email"
@@ -26470,7 +26437,6 @@
"$ref": "#/definitions/CommitUser"
},
"created": {
"description": "Created is the time when the commit was created",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -26610,7 +26576,6 @@
"x-go-name": "CanSign"
},
"created_at": {
"description": "The date and time when the GPG key was created",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -26624,7 +26589,6 @@
"x-go-name": "Emails"
},
"expires_at": {
"description": "The date and time when the GPG key expires",
"type": "string",
"format": "date-time",
"x-go-name": "Expires"
@@ -27113,7 +27077,6 @@
"x-go-name": "Config"
},
"created_at": {
"description": "The date and time when the webhook was created",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -27143,7 +27106,6 @@
"x-go-name": "Type"
},
"updated_at": {
"description": "The date and time when the webhook was last updated",
"type": "string",
"format": "date-time",
"x-go-name": "Updated"
@@ -27156,7 +27118,6 @@
"type": "object",
"properties": {
"email": {
"description": "Email is the person's email address",
"type": "string",
"format": "email",
"x-go-name": "Email"
@@ -27691,9 +27652,8 @@
"type": "string"
},
"Wiki": {
"description": "Is it a wiki page? (use mode=wiki instead)",
"type": "boolean",
"x-deprecated": true
"description": "Is it a wiki page? (use mode=wiki instead)\n\nDeprecated: true",
"type": "boolean"
}
},
"x-go-package": "gitea.dev/modules/structs"
@@ -27719,9 +27679,8 @@
"type": "string"
},
"Wiki": {
"description": "Is it a wiki page? (use mode=wiki instead)",
"type": "boolean",
"x-deprecated": true
"description": "Is it a wiki page? (use mode=wiki instead)\n\nDeprecated: true",
"type": "boolean"
}
},
"x-go-package": "gitea.dev/modules/structs"
@@ -27957,7 +27916,7 @@
"x-go-name": "OpenIssues"
},
"state": {
"description": "State indicates if the milestone is open or closed",
"description": "State indicates if the milestone is open or closed\nopen StateOpen pr is opened\nclosed StateClosed pr is closed",
"type": "string",
"enum": [
"open",
@@ -27994,6 +27953,142 @@
},
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfo": {
"description": "NodeInfo contains standardized way of exposing metadata about a server running one of the distributed social networks",
"type": "object",
"properties": {
"metadata": {
"description": "Metadata contains free form key value pairs for software specific values",
"type": "object",
"x-go-name": "Metadata"
},
"openRegistrations": {
"description": "OpenRegistrations indicates if new user registrations are accepted",
"type": "boolean",
"x-go-name": "OpenRegistrations"
},
"protocols": {
"description": "Protocols lists the protocols supported by this server",
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "Protocols"
},
"services": {
"$ref": "#/definitions/NodeInfoServices"
},
"software": {
"$ref": "#/definitions/NodeInfoSoftware"
},
"usage": {
"$ref": "#/definitions/NodeInfoUsage"
},
"version": {
"description": "Version specifies the schema version",
"type": "string",
"x-go-name": "Version"
}
},
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfoServices": {
"description": "NodeInfoServices contains the third party sites this server can connect to via their application API",
"type": "object",
"properties": {
"inbound": {
"description": "Inbound lists services that can deliver content to this server",
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "Inbound"
},
"outbound": {
"description": "Outbound lists services this server can deliver content to",
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "Outbound"
}
},
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfoSoftware": {
"description": "NodeInfoSoftware contains Metadata about server software in use",
"type": "object",
"properties": {
"homepage": {
"description": "Homepage is the URL to the homepage of this server software",
"type": "string",
"x-go-name": "Homepage"
},
"name": {
"description": "Name is the canonical name of this server software",
"type": "string",
"x-go-name": "Name"
},
"repository": {
"description": "Repository is the URL to the source code repository",
"type": "string",
"x-go-name": "Repository"
},
"version": {
"description": "Version is the version of this server software",
"type": "string",
"x-go-name": "Version"
}
},
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfoUsage": {
"description": "NodeInfoUsage contains usage statistics for this server",
"type": "object",
"properties": {
"localComments": {
"description": "LocalComments is the total amount of comments made by users local to this server",
"type": "integer",
"format": "int64",
"x-go-name": "LocalComments"
},
"localPosts": {
"description": "LocalPosts is the total amount of posts made by users local to this server",
"type": "integer",
"format": "int64",
"x-go-name": "LocalPosts"
},
"users": {
"$ref": "#/definitions/NodeInfoUsageUsers"
}
},
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfoUsageUsers": {
"description": "NodeInfoUsageUsers contains statistics about the users of this server",
"type": "object",
"properties": {
"activeHalfyear": {
"description": "ActiveHalfyear is the amount of users that signed in at least once in the last 180 days",
"type": "integer",
"format": "int64",
"x-go-name": "ActiveHalfyear"
},
"activeMonth": {
"description": "ActiveMonth is the amount of users that signed in at least once in the last 30 days",
"type": "integer",
"format": "int64",
"x-go-name": "ActiveMonth"
},
"total": {
"description": "Total is the total amount of users on this server",
"type": "integer",
"format": "int64",
"x-go-name": "Total"
}
},
"x-go-package": "gitea.dev/modules/structs"
},
"Note": {
"description": "Note contains information related to a git note",
"type": "object",
@@ -28042,7 +28137,7 @@
"x-go-name": "LatestCommentURL"
},
"state": {
"description": "State indicates the current state of the notification subject",
"description": "State indicates the current state of the notification subject\nopen NotifySubjectStateOpen is an open subject\nclosed NotifySubjectStateClosed is a closed subject\nmerged NotifySubjectStateMerged is a merged pull request",
"type": "string",
"enum": [
"open",
@@ -28058,7 +28153,7 @@
"x-go-name": "Title"
},
"type": {
"description": "Type indicates the type of the notification subject",
"description": "Type indicates the type of the notification subject\nIssue NotifySubjectIssue a issue is subject of an notification\nPull NotifySubjectPull a pull is subject of an notification\nCommit NotifySubjectCommit a commit is subject of an notification\nRepository NotifySubjectRepository a repository is subject of an notification",
"type": "string",
"enum": [
"Issue",
@@ -28220,7 +28315,7 @@
"x-go-name": "UserName"
},
"visibility": {
"description": "The visibility level of the organization (public, limited, private)",
"description": "The visibility level of the organization (public, limited, private)\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate",
"type": "string",
"enum": [
"public",
@@ -28306,7 +28401,6 @@
"type": "object",
"properties": {
"created_at": {
"description": "The date and time when the package was created",
"type": "string",
"format": "date-time",
"x-go-name": "CreatedAt"
@@ -28438,7 +28532,6 @@
"x-go-name": "Removed"
},
"timestamp": {
"description": "The timestamp when the commit was made",
"type": "string",
"format": "date-time",
"x-go-name": "Timestamp"
@@ -28591,7 +28684,6 @@
"type": "object",
"properties": {
"created_at": {
"description": "Created is the time when the key was added",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -28825,7 +28917,7 @@
"x-go-name": "ReviewComments"
},
"state": {
"description": "The current state of the pull request",
"description": "The current state of the pull request\nopen StateOpen pr is opened\nclosed StateClosed pr is closed",
"type": "string",
"enum": [
"open",
@@ -29168,7 +29260,6 @@
"x-go-name": "Reaction"
},
"created_at": {
"description": "The date and time when the reaction was created",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -29341,7 +29432,7 @@
"type": "object",
"properties": {
"permission": {
"description": "Permission level of the collaborator",
"description": "Permission level of the collaborator\nnone AccessLevelNameNone\nread AccessLevelNameRead\nwrite AccessLevelNameWrite\nadmin AccessLevelNameAdmin\nowner AccessLevelNameOwner",
"type": "string",
"enum": [
"none",
@@ -29640,7 +29731,7 @@
"x-go-name": "Name"
},
"object_format_name": {
"description": "ObjectFormatName of the underlying git repository",
"description": "ObjectFormatName of the underlying git repository\nsha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256",
"type": "string",
"enum": [
"sha1",
@@ -29837,7 +29928,6 @@
"type": "object",
"properties": {
"created": {
"description": "Created is the time when the stopwatch was started",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -29940,7 +30030,6 @@
"type": "object",
"properties": {
"created_at": {
"description": "The date and time when the tag protection was created",
"type": "string",
"format": "date-time",
"x-go-name": "Created"
@@ -29957,7 +30046,6 @@
"x-go-name": "NamePattern"
},
"updated_at": {
"description": "The date and time when the tag protection was last updated",
"type": "string",
"format": "date-time",
"x-go-name": "Updated"
@@ -30027,12 +30115,10 @@
"x-go-name": "Permission"
},
"units": {
"description": "Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.",
"type": "array",
"items": {
"type": "string"
},
"x-deprecated": true,
"x-go-name": "Units",
"example": [
"repo.code",
@@ -30063,7 +30149,7 @@
}
},
"visibility": {
"description": "Team visibility within the organization. \"private\" teams are only\nlistable by members and org owners; \"limited\" teams are listable by\nany organization member; \"public\" teams are listable by any signed-in\nuser.",
"description": "Team visibility within the organization. \"private\" teams are only\nlistable by members and org owners; \"limited\" teams are listable by\nany organization member; \"public\" teams are listable by any signed-in\nuser.\npublic TeamVisibilityPublic\nlimited TeamVisibilityLimited\nprivate TeamVisibilityPrivate",
"type": "string",
"enum": [
"public",
@@ -30583,7 +30669,7 @@
"x-go-name": "StarredRepos"
},
"visibility": {
"description": "User visibility level option: public, limited, private",
"description": "User visibility level option: public, limited, private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate",
"type": "string",
"enum": [
"public",
@@ -31415,6 +31501,12 @@
"type": "string"
}
},
"MergeUpstreamRequest": {
"description": "",
"schema": {
"$ref": "#/definitions/MergeUpstreamRequest"
}
},
"MergeUpstreamResponse": {
"description": "",
"schema": {
@@ -31436,6 +31528,12 @@
}
}
},
"NodeInfo": {
"description": "NodeInfo",
"schema": {
"$ref": "#/definitions/NodeInfo"
}
},
"Note": {
"description": "Note",
"schema": {
@@ -31613,6 +31711,12 @@
}
}
},
"Reference": {
"description": "Reference",
"schema": {
"$ref": "#/definitions/Reference"
}
},
"ReferenceList": {
"description": "ReferenceList",
"schema": {
@@ -31708,6 +31812,12 @@
"$ref": "#/definitions/SearchResults"
}
},
"Secret": {
"description": "Secret",
"schema": {
"$ref": "#/definitions/Secret"
}
},
"SecretList": {
"description": "SecretList",
"schema": {
@@ -31723,6 +31833,12 @@
"$ref": "#/definitions/ServerVersion"
}
},
"StopWatch": {
"description": "StopWatch",
"schema": {
"$ref": "#/definitions/StopWatch"
}
},
"StopWatchList": {
"description": "StopWatchList",
"schema": {
@@ -31969,6 +32085,15 @@
"notFound": {
"description": "APINotFound is a not found empty response"
},
"parameterBodies": {
"description": "parameterBodies",
"schema": {
"$ref": "#/definitions/LockIssueOption"
}
},
"redirect": {
"description": "APIRedirect is a redirect response"
},
"repoArchivedError": {
"description": "APIRepoArchivedError is an error that is raised when an archived repo should be modified",
"headers": {

View File

@@ -802,6 +802,16 @@
},
"description": "MarkupRender is a rendered markup document"
},
"MergeUpstreamRequest": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MergeUpstreamRequest"
}
}
},
"description": ""
},
"MergeUpstreamResponse": {
"content": {
"application/json": {
@@ -835,6 +845,16 @@
},
"description": "MilestoneList"
},
"NodeInfo": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NodeInfo"
}
}
},
"description": "NodeInfo"
},
"Note": {
"content": {
"application/json": {
@@ -1108,6 +1128,16 @@
},
"description": "ReactionList"
},
"Reference": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Reference"
}
}
},
"description": "Reference"
},
"ReferenceList": {
"content": {
"application/json": {
@@ -1257,6 +1287,16 @@
},
"description": "SearchResults"
},
"Secret": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Secret"
}
}
},
"description": "Secret"
},
"SecretList": {
"content": {
"application/json": {
@@ -1280,6 +1320,16 @@
},
"description": "ServerVersion"
},
"StopWatch": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StopWatch"
}
}
},
"description": "StopWatch"
},
"StopWatchList": {
"content": {
"application/json": {
@@ -1646,6 +1696,19 @@
"notFound": {
"description": "APINotFound is a not found empty response"
},
"parameterBodies": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LockIssueOption"
}
}
},
"description": "parameterBodies"
},
"redirect": {
"description": "APIRedirect is a redirect response"
},
"repoArchivedError": {
"description": "APIRepoArchivedError is an error that is raised when an archived repo should be modified",
"headers": {
@@ -2500,7 +2563,7 @@
"$ref": "#/components/schemas/RepoWritePermission"
}
],
"description": "Permission level to grant the collaborator"
"description": "Permission level to grant the collaborator\nread RepoWritePermissionRead\nwrite RepoWritePermissionWrite\nadmin RepoWritePermissionAdmin"
}
},
"type": "object",
@@ -2650,7 +2713,6 @@
"x-go-name": "DownloadURL"
},
"created_at": {
"description": "Created is the time when the attachment was uploaded",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -2800,8 +2862,9 @@
"x-go-name": "BlockOnRejectedReviews"
},
"branch_name": {
"deprecated": true,
"description": "Deprecated: true",
"type": "string",
"x-deprecated": true,
"x-go-name": "BranchName"
},
"bypass_allowlist_teams": {
@@ -3133,7 +3196,7 @@
"$ref": "#/components/schemas/CommitStatusState"
}
],
"description": "State is the overall combined status state"
"description": "State is the overall combined status state\npending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped is for when CommitStatus is Skipped"
},
"statuses": {
"description": "Statuses contains all individual commit statuses",
@@ -3239,7 +3302,6 @@
"$ref": "#/components/schemas/User"
},
"created": {
"description": "Created is the time when the commit was created",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -3306,13 +3368,11 @@
"description": "CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE",
"properties": {
"author": {
"description": "Author is the author date for the commit",
"format": "date-time",
"type": "string",
"x-go-name": "Author"
},
"committer": {
"description": "Committer is the committer date for the commit",
"format": "date-time",
"type": "string",
"x-go-name": "Committer"
@@ -3324,7 +3384,6 @@
"CommitMeta": {
"properties": {
"created": {
"description": "Created is the time when the commit was created",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -3403,7 +3462,7 @@
"$ref": "#/components/schemas/CommitStatusState"
}
],
"description": "State represents the status state (pending, success, error, failure)"
"description": "State represents the status state (pending, success, error, failure)\npending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped is for when CommitStatus is Skipped"
},
"target_url": {
"description": "TargetURL is the URL to link to for more details",
@@ -3445,7 +3504,6 @@
"x-go-name": "Date"
},
"email": {
"description": "Email is the person's email address",
"format": "email",
"type": "string",
"x-go-name": "Email"
@@ -3696,8 +3754,9 @@
"x-go-name": "BlockOnRejectedReviews"
},
"branch_name": {
"deprecated": true,
"description": "Deprecated: true",
"type": "string",
"x-deprecated": true,
"x-go-name": "BranchName"
},
"bypass_allowlist_teams": {
@@ -3851,10 +3910,10 @@
"x-go-name": "BranchName"
},
"old_branch_name": {
"description": "Name of the old branch to create from",
"deprecated": true,
"description": "Deprecated: true\nName of the old branch to create from",
"type": "string",
"uniqueItems": true,
"x-deprecated": true,
"x-go-name": "OldBranchName"
},
"old_ref_name": {
@@ -3977,7 +4036,6 @@
"properties": {
"active": {
"default": false,
"description": "Whether the webhook should be active upon creation",
"type": "boolean",
"x-go-name": "Active"
},
@@ -4008,7 +4066,6 @@
"x-go-name": "Name"
},
"type": {
"description": "The type of the webhook to create",
"enum": [
"dingtalk",
"discord",
@@ -4044,7 +4101,6 @@
"description": "CreateIssueCommentOption options for creating a comment on an issue",
"properties": {
"body": {
"description": "Body is the comment text content",
"type": "string",
"x-go-name": "Body"
}
@@ -4175,7 +4231,6 @@
"x-go-name": "IsArchived"
},
"name": {
"description": "Name is the display name for the new label",
"type": "string",
"x-go-name": "Name"
}
@@ -4196,18 +4251,12 @@
"x-go-name": "Description"
},
"due_on": {
"description": "Deadline is the due date for the milestone",
"format": "date-time",
"type": "string",
"x-go-name": "Deadline"
},
"state": {
"allOf": [
{
"$ref": "#/components/schemas/StateType"
}
],
"description": "State indicates the initial state of the milestone"
"$ref": "#/components/schemas/StateType"
},
"title": {
"description": "Title is the title of the new milestone",
@@ -4307,7 +4356,7 @@
"$ref": "#/components/schemas/UserVisibility"
}
],
"description": "possible values are `public` (default), `limited` or `private`"
"description": "possible values are `public` (default), `limited` or `private`\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate"
},
"website": {
"description": "The website URL of the organization",
@@ -4587,7 +4636,7 @@
"$ref": "#/components/schemas/ObjectFormatName"
}
],
"description": "ObjectFormatName of the underlying git repository, empty string for default (sha1)"
"description": "ObjectFormatName of the underlying git repository, empty string for default (sha1)\nsha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256"
},
"private": {
"description": "Whether the repository is private",
@@ -4641,7 +4690,7 @@
"$ref": "#/components/schemas/CommitStatusState"
}
],
"description": "State represents the status state to set (pending, success, error, failure)"
"description": "State represents the status state to set (pending, success, error, failure)\npending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped is for when CommitStatus is Skipped"
},
"target_url": {
"description": "TargetURL is the URL to link to for more details",
@@ -4662,7 +4711,6 @@
"x-go-name": "Message"
},
"tag_name": {
"description": "The name of the tag to create",
"type": "string",
"x-go-name": "TagName"
},
@@ -4732,15 +4780,13 @@
"$ref": "#/components/schemas/RepoWritePermission"
},
"units": {
"deprecated": true,
"description": "Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.",
"example": [
"repo.actions",
"repo.packages",
"repo.code",
"repo.issues",
"repo.ext_issues",
"repo.wiki",
"repo.ext_wiki",
"repo.pulls",
"repo.releases",
"repo.projects",
@@ -4750,25 +4796,13 @@
"type": "string"
},
"type": "array",
"x-deprecated": true,
"x-go-name": "Units"
},
"units_map": {
"additionalProperties": {
"type": "string"
},
"example": {
"repo.actions": "read",
"repo.code": "read",
"repo.ext_issues": "none",
"repo.ext_wiki": "none",
"repo.issues": "write",
"repo.packages": "read",
"repo.projects": "none",
"repo.pulls": "owner",
"repo.releases": "none",
"repo.wiki": "admin"
},
"example": "{\"repo.actions\",\"repo.packages\",\"repo.code\":\"read\",\"repo.issues\":\"write\",\"repo.ext_issues\":\"none\",\"repo.wiki\":\"admin\",\"repo.pulls\":\"owner\",\"repo.releases\":\"none\",\"repo.projects\":\"none\",\"repo.ext_wiki\":\"none\"}",
"type": "object",
"x-go-name": "UnitsMap"
},
@@ -4778,7 +4812,7 @@
"$ref": "#/components/schemas/TeamVisibility"
}
],
"description": "Team visibility within the organization. Defaults to \"private\"."
"description": "Team visibility within the organization. Defaults to \"private\".\npublic TeamVisibilityPublic\nlimited TeamVisibilityLimited\nprivate TeamVisibilityPrivate"
}
},
"required": [
@@ -4797,6 +4831,7 @@
"x-go-name": "Created"
},
"email": {
"format": "email",
"type": "string",
"x-go-name": "Email"
},
@@ -4848,7 +4883,7 @@
"$ref": "#/components/schemas/UserVisibility"
}
],
"description": "User visibility level: public, limited, or private"
"description": "User visibility level: public, limited, or private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate"
}
},
"required": [
@@ -5044,7 +5079,6 @@
"description": "DeployKey a deploy key",
"properties": {
"created_at": {
"description": "Created is the time when the deploy key was added",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -5379,7 +5413,6 @@
"description": "EditIssueCommentOption options for editing a comment",
"properties": {
"body": {
"description": "Body is the updated comment text content",
"type": "string",
"x-go-name": "Body"
}
@@ -5552,7 +5585,7 @@
"$ref": "#/components/schemas/UserVisibility"
}
],
"description": "possible values are `public`, `limited` or `private`"
"description": "possible values are `public`, `limited` or `private`\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate"
},
"website": {
"description": "The website URL of the organization",
@@ -5934,8 +5967,6 @@
"$ref": "#/components/schemas/RepoWritePermission"
},
"units": {
"deprecated": true,
"description": "Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.",
"example": [
"repo.code",
"repo.issues",
@@ -5950,7 +5981,6 @@
"type": "string"
},
"type": "array",
"x-deprecated": true,
"x-go-name": "Units"
},
"units_map": {
@@ -5976,7 +6006,7 @@
"$ref": "#/components/schemas/TeamVisibility"
}
],
"description": "Team visibility within the organization. When omitted, visibility is\nleft unchanged."
"description": "Team visibility within the organization. When omitted, visibility is\nleft unchanged.\npublic TeamVisibilityPublic\nlimited TeamVisibilityLimited\nprivate TeamVisibilityPrivate"
}
},
"required": [
@@ -6019,7 +6049,6 @@
"x-go-name": "Description"
},
"email": {
"description": "The email address of the user",
"format": "email",
"type": "string",
"x-go-name": "Email"
@@ -6067,7 +6096,6 @@
"x-go-name": "Restricted"
},
"source_id": {
"description": "The authentication source ID to associate with the user",
"format": "int64",
"type": "integer",
"x-go-name": "SourceID"
@@ -6078,7 +6106,7 @@
"$ref": "#/components/schemas/UserVisibility"
}
],
"description": "User visibility level: public, limited, or private"
"description": "User visibility level: public, limited, or private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate"
},
"website": {
"description": "The user's personal website URL",
@@ -6097,7 +6125,6 @@
"description": "Email an email address belonging to a user",
"properties": {
"email": {
"description": "The email address",
"format": "email",
"type": "string",
"x-go-name": "Email"
@@ -6177,7 +6204,6 @@
"$ref": "#/components/schemas/CommitUser"
},
"created": {
"description": "Created is the time when the commit was created",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -6320,7 +6346,6 @@
"x-go-name": "CanSign"
},
"created_at": {
"description": "The date and time when the GPG key was created",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -6334,7 +6359,6 @@
"x-go-name": "Emails"
},
"expires_at": {
"description": "The date and time when the GPG key expires",
"format": "date-time",
"type": "string",
"x-go-name": "Expires"
@@ -6827,7 +6851,6 @@
"x-go-name": "Config"
},
"created_at": {
"description": "The date and time when the webhook was created",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -6857,7 +6880,6 @@
"x-go-name": "Type"
},
"updated_at": {
"description": "The date and time when the webhook was last updated",
"format": "date-time",
"type": "string",
"x-go-name": "Updated"
@@ -6870,7 +6892,6 @@
"description": "Identity for a person's identity like an author or committer",
"properties": {
"email": {
"description": "Email is the person's email address",
"format": "email",
"type": "string",
"x-go-name": "Email"
@@ -7405,9 +7426,9 @@
"type": "string"
},
"Wiki": {
"description": "Is it a wiki page? (use mode=wiki instead)",
"type": "boolean",
"x-deprecated": true
"deprecated": true,
"description": "Is it a wiki page? (use mode=wiki instead)\n\nDeprecated: true",
"type": "boolean"
}
},
"type": "object",
@@ -7433,9 +7454,9 @@
"type": "string"
},
"Wiki": {
"description": "Is it a wiki page? (use mode=wiki instead)",
"type": "boolean",
"x-deprecated": true
"deprecated": true,
"description": "Is it a wiki page? (use mode=wiki instead)\n\nDeprecated: true",
"type": "boolean"
}
},
"type": "object",
@@ -7677,7 +7698,7 @@
"$ref": "#/components/schemas/StateType"
}
],
"description": "State indicates if the milestone is open or closed"
"description": "State indicates if the milestone is open or closed\nopen StateOpen pr is opened\nclosed StateClosed pr is closed"
},
"title": {
"description": "Title is the title of the milestone",
@@ -7708,6 +7729,142 @@
"type": "object",
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfo": {
"description": "NodeInfo contains standardized way of exposing metadata about a server running one of the distributed social networks",
"properties": {
"metadata": {
"description": "Metadata contains free form key value pairs for software specific values",
"type": "object",
"x-go-name": "Metadata"
},
"openRegistrations": {
"description": "OpenRegistrations indicates if new user registrations are accepted",
"type": "boolean",
"x-go-name": "OpenRegistrations"
},
"protocols": {
"description": "Protocols lists the protocols supported by this server",
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "Protocols"
},
"services": {
"$ref": "#/components/schemas/NodeInfoServices"
},
"software": {
"$ref": "#/components/schemas/NodeInfoSoftware"
},
"usage": {
"$ref": "#/components/schemas/NodeInfoUsage"
},
"version": {
"description": "Version specifies the schema version",
"type": "string",
"x-go-name": "Version"
}
},
"type": "object",
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfoServices": {
"description": "NodeInfoServices contains the third party sites this server can connect to via their application API",
"properties": {
"inbound": {
"description": "Inbound lists services that can deliver content to this server",
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "Inbound"
},
"outbound": {
"description": "Outbound lists services this server can deliver content to",
"items": {
"type": "string"
},
"type": "array",
"x-go-name": "Outbound"
}
},
"type": "object",
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfoSoftware": {
"description": "NodeInfoSoftware contains Metadata about server software in use",
"properties": {
"homepage": {
"description": "Homepage is the URL to the homepage of this server software",
"type": "string",
"x-go-name": "Homepage"
},
"name": {
"description": "Name is the canonical name of this server software",
"type": "string",
"x-go-name": "Name"
},
"repository": {
"description": "Repository is the URL to the source code repository",
"type": "string",
"x-go-name": "Repository"
},
"version": {
"description": "Version is the version of this server software",
"type": "string",
"x-go-name": "Version"
}
},
"type": "object",
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfoUsage": {
"description": "NodeInfoUsage contains usage statistics for this server",
"properties": {
"localComments": {
"description": "LocalComments is the total amount of comments made by users local to this server",
"format": "int64",
"type": "integer",
"x-go-name": "LocalComments"
},
"localPosts": {
"description": "LocalPosts is the total amount of posts made by users local to this server",
"format": "int64",
"type": "integer",
"x-go-name": "LocalPosts"
},
"users": {
"$ref": "#/components/schemas/NodeInfoUsageUsers"
}
},
"type": "object",
"x-go-package": "gitea.dev/modules/structs"
},
"NodeInfoUsageUsers": {
"description": "NodeInfoUsageUsers contains statistics about the users of this server",
"properties": {
"activeHalfyear": {
"description": "ActiveHalfyear is the amount of users that signed in at least once in the last 180 days",
"format": "int64",
"type": "integer",
"x-go-name": "ActiveHalfyear"
},
"activeMonth": {
"description": "ActiveMonth is the amount of users that signed in at least once in the last 30 days",
"format": "int64",
"type": "integer",
"x-go-name": "ActiveMonth"
},
"total": {
"description": "Total is the total amount of users on this server",
"format": "int64",
"type": "integer",
"x-go-name": "Total"
}
},
"type": "object",
"x-go-package": "gitea.dev/modules/structs"
},
"Note": {
"description": "Note contains information related to a git note",
"properties": {
@@ -7758,7 +7915,7 @@
"x-go-name": "LatestCommentURL"
},
"state": {
"description": "State indicates the current state of the notification subject",
"description": "State indicates the current state of the notification subject\nopen NotifySubjectStateOpen is an open subject\nclosed NotifySubjectStateClosed is a closed subject\nmerged NotifySubjectStateMerged is a merged pull request",
"enum": [
"open",
"closed",
@@ -7774,7 +7931,7 @@
"x-go-name": "Title"
},
"type": {
"description": "Type indicates the type of the notification subject",
"description": "Type indicates the type of the notification subject\nIssue NotifySubjectIssue a issue is subject of an notification\nPull NotifySubjectPull a pull is subject of an notification\nCommit NotifySubjectCommit a commit is subject of an notification\nRepository NotifySubjectRepository a repository is subject of an notification",
"enum": [
"Issue",
"Pull",
@@ -7952,7 +8109,7 @@
"$ref": "#/components/schemas/UserVisibility"
}
],
"description": "The visibility level of the organization (public, limited, private)"
"description": "The visibility level of the organization (public, limited, private)\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate"
},
"website": {
"description": "The website URL of the organization",
@@ -8030,7 +8187,6 @@
"description": "Package represents a package",
"properties": {
"created_at": {
"description": "The date and time when the package was created",
"format": "date-time",
"type": "string",
"x-go-name": "CreatedAt"
@@ -8163,7 +8319,6 @@
"x-go-name": "Removed"
},
"timestamp": {
"description": "The timestamp when the commit was made",
"format": "date-time",
"type": "string",
"x-go-name": "Timestamp"
@@ -8317,7 +8472,6 @@
"description": "PublicKey publickey is a user key to push code to repository",
"properties": {
"created_at": {
"description": "Created is the time when the key was added",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -8560,7 +8714,7 @@
"$ref": "#/components/schemas/StateType"
}
],
"description": "The current state of the pull request"
"description": "The current state of the pull request\nopen StateOpen pr is opened\nclosed StateClosed pr is closed"
},
"title": {
"description": "The title of the pull request",
@@ -8895,7 +9049,6 @@
"x-go-name": "Reaction"
},
"created_at": {
"description": "The date and time when the reaction was created",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -9079,7 +9232,7 @@
"$ref": "#/components/schemas/AccessLevelName"
}
],
"description": "Permission level of the collaborator"
"description": "Permission level of the collaborator\nnone AccessLevelNameNone\nread AccessLevelNameRead\nwrite AccessLevelNameWrite\nadmin AccessLevelNameAdmin\nowner AccessLevelNameOwner"
},
"role_name": {
"description": "RoleName is the name of the permission role",
@@ -9386,7 +9539,7 @@
"$ref": "#/components/schemas/ObjectFormatName"
}
],
"description": "ObjectFormatName of the underlying git repository"
"description": "ObjectFormatName of the underlying git repository\nsha1 ObjectFormatSHA1\nsha256 ObjectFormatSHA256"
},
"open_issues_count": {
"format": "int64",
@@ -9598,7 +9751,6 @@
"description": "StopWatch represent a running stopwatch",
"properties": {
"created": {
"description": "Created is the time when the stopwatch was started",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -9694,7 +9846,6 @@
"description": "TagProtection represents a tag protection",
"properties": {
"created_at": {
"description": "The date and time when the tag protection was created",
"format": "date-time",
"type": "string",
"x-go-name": "Created"
@@ -9711,7 +9862,6 @@
"x-go-name": "NamePattern"
},
"updated_at": {
"description": "The date and time when the tag protection was last updated",
"format": "date-time",
"type": "string",
"x-go-name": "Updated"
@@ -9772,8 +9922,6 @@
"$ref": "#/components/schemas/AccessLevelName"
},
"units": {
"deprecated": true,
"description": "Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.",
"example": [
"repo.code",
"repo.issues",
@@ -9788,7 +9936,6 @@
"type": "string"
},
"type": "array",
"x-deprecated": true,
"x-go-name": "Units"
},
"units_map": {
@@ -9814,7 +9961,7 @@
"$ref": "#/components/schemas/TeamVisibility"
}
],
"description": "Team visibility within the organization. \"private\" teams are only\nlistable by members and org owners; \"limited\" teams are listable by\nany organization member; \"public\" teams are listable by any signed-in\nuser."
"description": "Team visibility within the organization. \"private\" teams are only\nlistable by members and org owners; \"limited\" teams are listable by\nany organization member; \"public\" teams are listable by any signed-in\nuser.\npublic TeamVisibilityPublic\nlimited TeamVisibilityLimited\nprivate TeamVisibilityPrivate"
}
},
"type": "object",
@@ -10346,7 +10493,7 @@
"$ref": "#/components/schemas/UserVisibility"
}
],
"description": "User visibility level option: public, limited, private"
"description": "User visibility level option: public, limited, private\npublic UserVisibilityPublic\nlimited UserVisibilityLimited\nprivate UserVisibilityPrivate"
},
"website": {
"description": "the user's website",

View File

@@ -344,59 +344,6 @@ jobs:
})
})
t.Run("Filtered required scoped check passes as skipped and allows merge", func(t *testing.T) {
// A required scoped workflow excluded by a paths filter posts a skipped (success) commit status,
// so the required check is satisfied and the PR can merge.
const scopedFilteredPRWorkflow = `name: Scoped Filtered PR
on:
pull_request:
paths:
- src/**
jobs:
scoped-filtered-job:
runs-on: ubuntu-latest
steps:
- run: echo scoped-filtered
`
source := createTestRepo(t, "sw-filtered-source", false)
createRepoWorkflowFile(t, user2, user2Token, source, ".gitea/scoped_workflows/pr.yaml", scopedFilteredPRWorkflow)
registerUserScopedSource(t, source, "pr.yaml") // required
consumer := createTestRepo(t, "sw-filtered-consumer", false)
// Protect the default branch (its own status check stays off, so only the required scoped check gates the merge).
user2Session.MakeRequest(t, NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/edit", consumer.OwnerName, consumer.Name), map[string]string{
"rule_name": consumer.DefaultBranch,
"enable_push": "true",
"block_admin_merge_override": "true", // otherwise the repo owner bypasses the status check
}), http.StatusSeeOther)
// Open a PR that changes a file NOT matching the workflow's `paths: [src/**]`, so it is filtered out.
prFile := &api.CreateFileOptions{
FileOptions: api.FileOptions{
BranchName: consumer.DefaultBranch, NewBranchName: "filtered-pr", Message: "pr change",
Author: api.Identity{Name: user2.Name, Email: user2.Email},
Committer: api.Identity{Name: user2.Name, Email: user2.Email},
Dates: api.CommitDateOptions{Author: time.Now(), Committer: time.Now()},
},
ContentBase64: base64.StdEncoding.EncodeToString([]byte("pr change")),
}
createWorkflowFile(t, user2Token, consumer.OwnerName, consumer.Name, "docs.txt", prFile)
apiCtx := NewAPITestContext(t, user2.Name, consumer.Name, auth_model.AccessTokenScopeWriteRepository)
pr, err := doAPICreatePullRequest(apiCtx, consumer.OwnerName, consumer.Name, consumer.DefaultBranch, "filtered-pr")(t)
require.NoError(t, err)
// Filtered: no scoped run is created, but a skipped commit status is posted on the PR head.
assert.Equal(t, 0, unittest.GetCount(t, &actions_model.ActionRun{RepoID: consumer.ID, IsScopedRun: true}), "filtered scoped workflow creates no run")
assertSkippedCommitStatusExists(t, consumer.ID, pr.Head.Sha, "pull_request")
// The skipped (success) status satisfies the required scoped check (prefixed with the source repo), so the merge is allowed.
assert.NoError(t, queue.GetManager().FlushAll(t.Context(), 5*time.Second))
mergeReq := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", consumer.OwnerName, consumer.Name, pr.Index),
&forms.MergePullRequestForm{Do: string(repo_model.MergeStyleMerge), MergeMessageField: "merge"}).AddTokenAuth(user2Token)
user2Session.MakeRequest(t, mergeReq, http.StatusOK)
})
t.Run("Settings page required patterns", func(t *testing.T) {
source := createTestRepo(t, "sw-settings-source", false)
createRepoWorkflowFile(t, user2, user2Token, source, ".gitea/scoped_workflows/push.yaml", scopedPushWorkflow)

View File

@@ -215,9 +215,8 @@ jobs:
err = pull_service.NewPullRequest(t.Context(), prOpts)
assert.NoError(t, err)
// the new pull request is filtered by paths, so no run is created; a skipped commit status is posted instead
// the new pull request cannot trigger actions, so there is still only 1 record
assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: baseRepo.ID}))
assertSkippedCommitStatusExists(t, baseRepo.ID, addFileToForkedResp.Commit.SHA, "pull_request_target")
})
}
@@ -339,9 +338,6 @@ jobs:
})
assert.NoError(t, err)
assert.NotEmpty(t, addFileToBranchResp)
// the push to test-skip-ci is filtered by branches, so no run is created; a skipped commit status is posted instead
assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
assertSkippedCommitStatusExists(t, repo.ID, addFileToBranchResp.Commit.SHA, "push")
resp := testPullCreate(t, session, "user2", "skip-ci", true, "master", "test-skip-ci", "[skip ci] test-skip-ci")
@@ -349,7 +345,7 @@ jobs:
url := test.RedirectURL(resp)
assert.Regexp(t, "^/user2/skip-ci/pulls/[0-9]*$", url)
// the pr title contains a configured skip-ci string, so no run and no skipped status are created
// the pr title contains a configured skip-ci string, so there is still only 1 record
assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
})
}
@@ -1883,16 +1879,3 @@ jobs:
runner.fetchNoTask(t)
})
}
// assertSkippedCommitStatusExists asserts that a filtered-out workflow posted a skipped commit status on sha
func assertSkippedCommitStatusExists(t *testing.T, repoID int64, sha, eventSuffix string) {
t.Helper()
statuses, err := git_model.GetLatestCommitStatus(t.Context(), repoID, sha, db.ListOptionsAll)
require.NoError(t, err)
for _, s := range statuses {
if s.State == commitstatus.CommitStatusSkipped && strings.Contains(s.Context, "("+eventSuffix+")") {
return
}
}
assert.Failf(t, "missing skipped commit status", "no skipped commit status with event %q on %s (found %d statuses)", eventSuffix, sha, len(statuses))
}

View File

@@ -241,27 +241,3 @@ func TestGetFileHistoryNotOnMaster(t *testing.T) {
assert.Equal(t, "1", resp.Header().Get("X-Total"))
}
func TestGetFileHistoryEmptyDateRange(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// Login as User2.
session := loginUser(t, user.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
// readme.md exists in repo16 but no commits fall before 1970, so the date
// filter yields an empty range: this must return 200 with an empty list,
// not 404 (regression: a valid path with an empty date range was a 404).
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/commits?path=readme.md&sha=good-sign&until=1970-01-01T00:00:00Z", user.Name).
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
apiData := DecodeJSON(t, resp, []api.Commit{})
assert.Empty(t, apiData)
assert.Equal(t, "0", resp.Header().Get("X-Total"))
// a path that does not exist must still return 404 even with a date filter
req = NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/commits?path=does-not-exist.md&sha=good-sign&until=1970-01-01T00:00:00Z", user.Name).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
}

View File

@@ -171,11 +171,6 @@ func testGetAttachment(t *testing.T) {
{"PrivateAccessibleByUser", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, user2Session, http.StatusOK},
{"RepoNotAccessibleByUser", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, user8Session, http.StatusNotFound},
{"OrgNotAccessibleByUser", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a21", true, user8Session, http.StatusNotFound},
// draft release attachments must only be reachable by users with write access, even on a public repo
{"DraftReleaseByOwner", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a23", true, user2Session, http.StatusOK},
{"DraftReleaseByAdmin", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a23", true, adminSession, http.StatusOK},
{"DraftReleaseByNonCollaborator", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a23", true, user8Session, http.StatusNotFound},
{"DraftReleaseByAnonymous", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a23", true, emptySession, http.StatusNotFound},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {

View File

@@ -22,7 +22,6 @@ import (
"gitea.dev/services/auth/source/oauth2"
"gitea.dev/tests"
"github.com/pquerna/otp/totp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/builder"
@@ -490,73 +489,3 @@ func TestOAuth2GroupClaimsManualLinking(t *testing.T) {
})
}
}
// TestOAuth2AutoLinkWithTwoFactor verifies that automatic account linking completes
// after the user passes local 2FA when an OIDC identity matches an existing account.
func TestOAuth2AutoLinkWithTwoFactor(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer test.MockVariableValue(&setting.OAuth2Client.EnableAutoRegistration, true)()
defer test.MockVariableValue(&setting.OAuth2Client.AccountLinking, setting.OAuth2AccountLinkingAuto)()
defer test.MockVariableValue(&setting.OAuth2Client.Username, setting.OAuth2UsernameEmail)()
const (
sourceName = "test-oauth-auto-link-2fa"
sub = "oidc-auto-link-2fa-sub"
email = "oidc-auto-link-2fa@example.com"
userName = "oidc-auto-link-2fa"
)
srv := newFakeOIDCServer(t, FakeOIDCConfig{Sub: sub, Email: email, Name: "OIDC Auto Link 2FA"})
addOAuth2Source(t, sourceName, oauth2.Source{
Provider: "openidConnect",
ClientID: "test-client-id",
ClientSecret: "test-client-secret",
OpenIDConnectAutoDiscoveryURL: srv.URL + "/.well-known/openid-configuration",
})
authSource, err := auth_model.GetActiveOAuth2SourceByAuthName(t.Context(), sourceName)
require.NoError(t, err)
localUser := &user_model.User{Name: userName, Email: email}
require.NoError(t, user_model.CreateUser(t.Context(), localUser, &user_model.Meta{}))
otpKey, err := totp.Generate(totp.GenerateOpts{
SecretSize: 40,
Issuer: "gitea-test",
AccountName: localUser.Name,
})
require.NoError(t, err)
tfa := &auth_model.TwoFactor{UID: localUser.ID}
require.NoError(t, tfa.SetSecret(otpKey.Secret()))
require.NoError(t, auth_model.NewTwoFactor(t.Context(), tfa))
unittest.AssertNotExistsBean(t, &user_model.ExternalLoginUser{ExternalID: sub, LoginSourceID: authSource.ID}, unittest.OrderBy("external_id ASC"))
session := emptyTestSession(t)
resp := session.MakeRequest(t, NewRequest(t, "GET", "/user/oauth2/"+sourceName), http.StatusTemporaryRedirect)
location := resp.Header().Get("Location")
u, err := url.Parse(location)
require.NoError(t, err)
state := u.Query().Get("state")
require.NotEmpty(t, state)
callbackURL := fmt.Sprintf("/user/oauth2/%s/callback?code=test-code&state=%s", sourceName, url.QueryEscape(state))
resp = session.MakeRequest(t, NewRequest(t, "GET", callbackURL), http.StatusSeeOther)
assert.Contains(t, resp.Header().Get("Location"), "/user/two_factor")
session.MakeRequest(t, NewRequest(t, "GET", "/user/two_factor"), http.StatusOK)
passcode, err := totp.GenerateCode(otpKey.Secret(), time.Now())
require.NoError(t, err)
req := NewRequestWithValues(t, "POST", "/user/two_factor", map[string]string{
"passcode": passcode,
})
session.MakeRequest(t, req, http.StatusSeeOther)
externalLink := unittest.AssertExistsAndLoadBean(t, &user_model.ExternalLoginUser{ExternalID: sub, LoginSourceID: authSource.ID}, unittest.OrderBy("external_id ASC"))
assert.Equal(t, localUser.ID, externalLink.UserID)
session.MakeRequest(t, NewRequest(t, "GET", "/user/settings"), http.StatusOK)
}

View File

@@ -10,7 +10,6 @@ import (
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
"gitea.dev/modules/translation"
@@ -40,10 +39,11 @@ func createNewRelease(t *testing.T, session *TestSession, repoURL, tag, title st
if draft {
postData["draft"] = "1"
}
req = NewRequestWithValues(t, "POST", link, postData)
resp = session.MakeRequest(t, req, http.StatusOK)
assert.NotEmpty(t, test.ParseJSONRedirect(resp.Body.Bytes()))
resp = session.MakeRequest(t, req, http.StatusSeeOther)
test.RedirectURL(resp) // check that redirect URL exists
}
func checkLatestReleaseAndCount(t *testing.T, session *TestSession, repoURL, version, label string, count int) {
@@ -253,27 +253,3 @@ func TestDownloadReleaseAttachment(t *testing.T) {
session := loginUser(t, "user2")
session.MakeRequest(t, req, http.StatusOK)
}
func TestEditReleaseAttachmentRejectsForbiddenRename(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer test.MockVariableValue(&setting.Repository.Release.AllowedTypes, ".zip")()
attachment := unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: 9})
release := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{ID: attachment.ReleaseID})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: attachment.RepoID})
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
session := loginUser(t, repoOwner.Name)
req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/releases/edit/%s", repo.Link(), release.TagName), map[string]string{
"title": release.Title,
"content": release.Note,
"attachment-edit-" + attachment.UUID: "evil.exe",
})
resp := session.MakeRequest(t, req, http.StatusBadRequest)
errMsg := test.ParseJSONError(resp.Body.Bytes()).ErrorMessage
assert.Equal(t, "This file cannot be uploaded or modified due to a forbidden file extension or type.", errMsg)
attachment = unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: attachment.ID})
assert.NotEqual(t, "evil.exe", attachment.Name)
}

24
types.d.ts vendored
View File

@@ -1,3 +1,9 @@
declare module 'eslint-plugin-array-func' {
import type {Eslint} from 'eslint';
const plugin: Eslint.Plugin;
export = plugin;
}
declare module '*.svg' {
const value: string;
export default value;
@@ -41,3 +47,21 @@ declare module '@citation-js/core' {
declare module '@citation-js/plugin-software-formats' {}
declare module '@citation-js/plugin-bibtex' {}
declare module '@citation-js/plugin-csl' {}
declare module 'vue-bar-graph' {
import type {DefineComponent} from 'vue';
interface BarGraphPoint {
value: number;
label: string;
}
export const VueBarGraph: DefineComponent<{
points?: Array<BarGraphPoint>;
barColor?: string;
textColor?: string;
textAltColor?: string;
height?: number;
labelHeight?: number;
}>;
}

View File

@@ -2,7 +2,7 @@
import {computed, nextTick, onBeforeUnmount, onMounted, ref, toRefs, watch} from 'vue';
import {SvgIcon} from '../svg.ts';
import ActionStatusIcon from './ActionStatusIcon.vue';
import {addDelegatedEventListener, createElementFromAttrs} from '../utils/dom.ts';
import {addDelegatedEventListener, createElementFromAttrs, toggleElem} from '../utils/dom.ts';
import {formatDatetime, formatDatetimeISO} from '../utils/time.ts';
import {POST} from '../modules/fetch.ts';
import {copyToClipboardWithFeedback} from '../modules/clipboard.ts';
@@ -247,6 +247,9 @@ function createLogLine(stepIndex: number, startTime: number, line: LogLine, cmd:
`${seconds}s`, // for "Show seconds"
);
toggleElem(logTimeStamp, timeVisible.value['log-time-stamp']);
toggleElem(logTimeSeconds, timeVisible.value['log-time-seconds']);
const lineClass = cmd?.name ? `job-log-line log-line-${cmd.name}` : 'job-log-line';
return createElementFromAttrs('div', {id: `jobstep-${stepIndex}-${line.index}`, class: lineClass},
lineNum, logTimeStamp, logMsg, logTimeSeconds,
@@ -388,6 +391,9 @@ function elStepsContainer(): HTMLElement {
function toggleTimeDisplay(type: 'seconds' | 'stamp') {
timeVisible.value[`log-time-${type}`] = !timeVisible.value[`log-time-${type}`];
for (const el of elStepsContainer().querySelectorAll(`.log-time-${type}`)) {
toggleElem(el, timeVisible.value[`log-time-${type}`]);
}
saveLocaleStorageOptions();
}
@@ -467,15 +473,7 @@ async function hashChangeListener() {
</div>
</div>
<!-- always create the node because we have our own event listeners on it, don't use "v-if" -->
<div
class="job-step-container"
ref="stepsContainer"
v-show="!isCallerJob && currentJob.steps.length"
:class="{
'log-line-show-timestamps': timeVisible['log-time-stamp'],
'log-line-show-seconds': timeVisible['log-time-seconds']
}"
>
<div class="job-step-container" ref="stepsContainer" v-show="!isCallerJob && currentJob.steps.length">
<div class="job-step-section" v-for="(jobStep, stepIdx) in currentJob.steps" :key="stepIdx">
<div
class="job-step-summary"
@@ -683,22 +681,8 @@ async function hashChangeListener() {
scroll-margin-top: 95px;
}
.job-log-line .log-time-stamp,
.job-log-line .log-time-seconds {
display: none;
}
.log-line-show-timestamps .job-log-line .log-time-stamp {
display: inline;
}
.log-line-show-seconds .job-log-line .log-time-seconds {
display: inline;
}
/* class names 'log-time-seconds' and 'log-time-stamp' are used in the method toggleTimeDisplay */
.job-log-line .line-num,
.job-log-line .log-time-seconds {
.job-log-line .line-num, .log-time-seconds {
width: 48px;
color: var(--color-text-light-3);
text-align: right;
@@ -715,16 +699,16 @@ async function hashChangeListener() {
}
.job-log-line .log-time,
.job-log-line .log-time-stamp {
.log-time-stamp {
color: var(--color-text-light-3);
margin-left: 12px;
margin-left: 10px;
white-space: nowrap;
}
.job-step-logs .job-log-line .log-msg {
flex: 1;
white-space: break-spaces;
margin-left: 12px;
margin-left: 10px;
overflow-wrap: anywhere;
}
@@ -791,28 +775,30 @@ async function hashChangeListener() {
border-radius: 0;
}
.job-log-group .job-log-list .job-log-line .log-msg {
margin-left: 2em;
}
.job-log-group-summary {
cursor: pointer;
list-style: none; /* hide the standard disclosure marker (Chrome, Edge, Firefox) */
position: relative;
display: list-item;
list-style: disclosure-closed inside;
padding-left: 58px; /* line-num gutter (48px) + log-msg margin (10px), so the marker sits in the content column */
}
.job-log-group-summary::-webkit-details-marker { /* hide the disclosure marker on Safari */
display: none;
.job-log-group[open] > .job-log-group-summary {
list-style-type: disclosure-open;
}
.log-line-group .log-msg::before {
content: "";
display: inline-block;
vertical-align: middle;
margin-top: -2.5px;
margin-right: 8px;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 6px solid var(--color-text-light-3);
transition: transform 0.1s ease;
.job-log-group-summary > .job-log-line {
position: absolute;
inset: 0;
z-index: -1; /* sit behind the disclosure marker */
overflow: hidden;
}
.job-log-group[open] .log-line-group .log-msg::before {
transform: rotate(90deg);
.job-log-group-summary > .job-log-line .log-msg {
margin-left: 21px;
}
</style>

View File

@@ -1,13 +1,6 @@
<script lang="ts" setup>
import {onMounted, shallowRef, useTemplateRef, type ShallowRef} from 'vue';
const barSlotWidth = 40; // horizontal space allotted per author
const chartHeight = 100; // keep in sync with reserved height in template
const innerChartHeight = chartHeight - 28; // 28 = avatar/x-axis label row (20) + 8px padding
const barMidPoint = barSlotWidth / 2;
const barWidth = barSlotWidth - 2; // 2px gap between bars
const avatarSize = 20;
const labelInsideThreshold = 22; // bars at least this tall carry the commit count inside them
import {VueBarGraph} from 'vue-bar-graph';
import {computed, onMounted, shallowRef, useTemplateRef, type ShallowRef} from 'vue';
const colors = shallowRef({
barColor: 'green',
@@ -25,19 +18,26 @@ type ActivityAuthorData = {
const activityTopAuthors: Array<ActivityAuthorData> = window.config.pageData.repoActivityTopAuthors || [];
const graphWidth = activityTopAuthors.length * barSlotWidth;
const maxCommits = Math.max(...activityTopAuthors.map((author) => author.commits));
const graphPoints = computed(() => {
return activityTopAuthors.map((item) => {
return {
value: item.commits,
label: item.name,
};
});
});
const bars = activityTopAuthors.map((author, index) => {
const height = author.commits / maxCommits * innerChartHeight;
return {
author,
index,
x: index * barSlotWidth,
height,
yOffset: innerChartHeight - height,
labelInside: height >= labelInsideThreshold,
};
const graphAuthors = computed(() => {
return activityTopAuthors.map((item, idx: number) => {
return {
position: idx + 1,
...item,
};
});
});
const graphWidth = computed(() => {
return activityTopAuthors.length * 40;
});
const styleElement = useTemplateRef('styleElement') as Readonly<ShallowRef<HTMLDivElement>>;
@@ -59,34 +59,49 @@ onMounted(() => {
<div>
<div class="activity-bar-graph tw-w-0 tw-h-0" ref="styleElement"/>
<div class="activity-bar-graph-alt tw-w-0 tw-h-0" ref="altStyleElement"/>
<svg :width="graphWidth" :height="chartHeight">
<g v-for="bar in bars" :key="bar.index" :transform="`translate(${bar.x},0)`">
<title>{{ bar.author.name }}</title>
<rect :width="barWidth" :height="bar.height" :x="2" :y="bar.yOffset" :style="{fill: colors.barColor}"/>
<text
:x="barMidPoint"
:y="bar.yOffset"
:dy="bar.labelInside ? '15px' : '-5px'"
text-anchor="middle"
:style="{fill: bar.labelInside ? colors.textAltColor : colors.textColor, font: '10px sans-serif'}"
>{{ bar.author.commits }}</text>
<a v-if="bar.author.home_link" :href="bar.author.home_link">
<image :x="barMidPoint - avatarSize / 2" :y="innerChartHeight + 4" :height="avatarSize" :width="avatarSize" :href="bar.author.avatar_link"/>
</a>
<image v-else :x="barMidPoint - avatarSize / 2" :y="innerChartHeight + 4" :height="avatarSize" :width="avatarSize" :href="bar.author.avatar_link"/>
<line class="axis-line" :x1="barMidPoint" :x2="barMidPoint" :y1="innerChartHeight + 3" :y2="innerChartHeight"/>
</g>
<line class="axis-line" :x1="2" :x2="graphWidth" :y1="innerChartHeight" :y2="innerChartHeight"/>
</svg>
<vue-bar-graph
:points="graphPoints"
:show-x-axis="true"
:show-y-axis="false"
:show-values="true"
:width="graphWidth"
:bar-color="colors.barColor"
:text-color="colors.textColor"
:text-alt-color="colors.textAltColor"
:height="100"
:label-height="20"
>
<template #label="opt">
<g v-for="(author, idx) in graphAuthors" :key="author.position">
<a
v-if="opt.bar.index === idx && author.home_link"
:href="author.home_link"
>
<image
:x="`${opt.bar.midPoint - 10}px`"
:y="`${opt.bar.yLabel}px`"
height="20"
width="20"
:href="author.avatar_link"
/>
</a>
<image
v-else-if="opt.bar.index === idx"
:x="`${opt.bar.midPoint - 10}px`"
:y="`${opt.bar.yLabel}px`"
height="20"
width="20"
:href="author.avatar_link"
/>
</g>
</template>
<template #title="opt">
<tspan v-for="(author, idx) in graphAuthors" :key="author.position">
<tspan v-if="opt.bar.index === idx">
{{ author.name }}
</tspan>
</tspan>
</template>
</vue-bar-graph>
</div>
</template>
<style scoped>
svg {
display: block; /* avoid the inline-baseline gap so the reserved container height matches exactly */
}
.axis-line {
stroke: var(--color-secondary-alpha-60);
stroke-width: 1;
}
</style>

View File

@@ -125,7 +125,7 @@ export async function initDropzone(dropzoneEl: HTMLElement) {
dzInst.removeAllFiles(true);
disableRemovedfileEvent = false;
dropzoneEl.querySelector('.files')!.replaceChildren();
dropzoneEl.querySelector('.files')!.innerHTML = '';
for (const el of dropzoneEl.querySelectorAll('.dz-preview')) el.remove();
fileUuidDict = {};
for (const attachment of respData) {