Compare commits

..

134 Commits

Author SHA1 Message Date
John Olheiser 2f73fff053 Changelog for 1.9.6 (#8967)
* Changelog for 1.9.6

Signed-off-by: jolheiser <john.olheiser@gmail.com>
2019-11-13 20:49:02 +00:00
zeripath fb5af37b3e Add Close() method to gogitRepository (#8901) (#8958)
Backport #8901 - Adjusted slightly for 1.9

In investigating #7947 it has become clear that the storage component of go-git repositories needs closing.

This PR adds this Close function and adds the Close functions as necessary.

In TransferOwnership the ctx.Repo.GitRepo is closed if it is open to help prevent the risk of multiple open files.

Fixes #7947
2019-11-13 18:51:33 +00:00
mrsdizzie 2ef37522b6 Update Github Migration Test (#8946)
Small fix for test on 1.9 since #8938 can't be easily back ported to
this branch.
2019-11-13 12:33:33 +08:00
zeripath 46ff639a13 Fix issue with user.fullname (#8904) 2019-11-10 00:22:21 +00:00
guillep2k 0b91aaf62a Update Github migration test (#8897)
Earlier today #716 was reopened which updated the modification time for
an old milestone (1.6.0) that we use in testing with the assumption that
it is old and won't change. This breaks all builds now, so remove this
test since we have others that test the same code and this milestone
will likely be updated again as that issue changes etc...
2019-11-09 15:14:40 -05:00
Lunny Xiao 4a08d574cf fix 500 when edit hook (#8782) (#8790) 2019-11-02 14:31:29 -04:00
zeripath f104898989 Allow to merge if file path contains " or \ (#8629) (#8772)
* if a filename in a repository contains " or \ the owner can't merge pull request with this files
because "git diff-tree" adds double quotes to that filepath
example: filepath is ab"cd but "git diff-tree" returns "ab\"cd"

now, when the owner click "Merge Pull Request" button the server returns 500
this commit fix it

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* add -z option to getDiffTree
escape spec symbols for sparse-checkout

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* go fmt

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* typo

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* escape '\'
escape all spaces and '!'

* use regexp.ReplaceAllString()

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* strings.ReplaceAll was added in go 1.12

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* add '\' to regexp.MustCompile

Signed-off-by: Ilya Pavlov <ilux@cpan.org>
2019-11-01 13:50:49 -04:00
John Olheiser 83d04df365 Changelog 1.9.5 (#8753)
* 1.9.5

Signed-off-by: jolheiser <john.olheiser@gmail.com>
2019-10-30 17:40:15 +00:00
6543 9614bb1b9f [Backport] [Fix] milestone close timestamp (#8728) (#8731)
* [Fix] milestone close timestamp (#8728)

* BugFix: Update closed_date_unix colum on milestone table on close

* use go standart time lib

* make backport work!
2019-10-29 13:24:24 +08:00
David Svantesson b2c3a7d79f Fix deadline on update issue or PR via API (#8699) 2019-10-28 01:36:28 +02:00
jaqra 76bbcf1387 make call createMilestoneComment on newIssue func (#8678) (#8682)
* make call createMilestoneComment on newIssue func

* make OldMilestoneID 0 instead of -1
2019-10-25 11:10:28 +01:00
Lunny Xiao 1bcbc02045 Revert "API should follow RequireSignInView (#8654) (#8661)" (#8674)
This reverts commit ffff835b73.
2019-10-24 22:07:13 +01:00
Monty Taylor 66ceee08dc Fix 500 when getting user as unauthenticated user (#8662)
When doing GET /api/v1/users/{user} as an unauthenticated user,
gitea throws a 500 because it's trying to dereference elements
from the context user. It wants to do this to see whether to
show the primary email and will do that if the logged in user
is admin or the user in question. However, if ctx.User is nil,
go gets really unhappy.
2019-10-24 16:25:30 +08:00
Lunny Xiao ffff835b73 API should follow RequireSignInView (#8654) (#8661) 2019-10-24 14:57:24 +08:00
Lunny Xiao 63c54f7e1f Hide some user information via API if user have no enough permission (#8655) (#8658)
* Hide some user information via API if user have no enough permission

* fix test
2019-10-24 09:01:40 +03:00
zeripath f9845454cf Use AppSubUrl for more redirections (#8647) (#8652)
Partial backport without changes to locale files.

Fix #8461 - fix misspelling of {{AppSubUrl}} and other misspelling in template
Fixes /explore and organisation redirection
2019-10-23 18:26:54 -04:00
John Olheiser 1e1211c194 Add SubURL to redirect path (#8632) (#8634) (#8640)
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2019-10-23 07:11:29 +01:00
zeripath 10e549df7d Update heatmap fixtures to restore tests (#8615) (#8617)
* Update heatmap fixtures to restore tests
* Add hint to check the fixture age on fail
2019-10-21 22:19:27 +01:00
6543 519f69eb41 Fix #8582 by handling empty repos (#8587) (#8593)
* Fix #8582 by handling empty repos

Signed-off-by: Jonas Franz <info@jonasfranz.software>

* Fix tests

Signed-off-by: Jonas Franz <info@jonasfranz.software>
2019-10-19 22:07:12 +03:00
Lunny Xiao d4501ece55 fix bug pull request files will be broken if head repo was transfered to another user or orgnization (#8571) 2019-10-18 16:14:37 +08:00
zeripath c1152b15fe Add missed close in ServeBlobLFS (#8527) (#8543) 2019-10-17 00:05:23 +08:00
zeripath cb31f88383 Fixes #8369: Create .ssh dir as necessary (#8486) (#8489)
* Ensure .ssh dir exists before rewriting public keys

* Ensure .ssh dir exists before appending to authorized_keys

* Log the error because it would be useful to know where it is trying to MkdirAll

* Only try to create RootPath if it's not empty
2019-10-13 23:01:52 +03:00
zeripath 6cb9ce1367 IsBranchExist: return false if provided name is empty (#8485) (#8492)
* IsBranchExist: return false if provided name is empty

* Ensure that the reference returned is actually of a valid type
2019-10-13 20:23:43 +03:00
guillep2k d93d5d7906 Backport: Ignore mentions for users with no access (#8395) (#8484)
* Ignore mentions for users with no access

* Fix fmt
2019-10-13 16:17:53 +01:00
zeripath 5c3863c319 Restore functionality for early gits (#7775) (#8476)
* Change tests to make it possible to run TestGit with 1.7.2

* Make merge run on 1.7.2

* Fix tracking and staging branch name problem

* Ensure that git 1.7.2 works on tests

* ensure that there is no chance for conflicts

* Fix-up missing merge issues

* Final rm

* Ensure LFS filters run on the tests

* Do not sign commits from temp repo

* Apply suggestions from code review

* Update modules/repofiles/temp_repo.go
2019-10-13 12:40:13 +08:00
guillep2k 80b50afe1f Add check for empty set when dropping indexes during migration (#8475) 2019-10-12 06:31:12 +01:00
zeripath 20a28b785a Ensure Request Body Readers are closed in LFS server (#8454) (#8459) 2019-10-11 17:11:06 +01:00
zeripath d330b2f52b Ensure that LFS files are relative to the LFS content path (#8455) (#8458) 2019-10-11 13:41:55 +01:00
6543 6ab1d5a8a0 Changelog for v1.9.4 (#8422)
* changelog

* Update CHANGELOG.md

We ned to revert this then ...

Co-Authored-By: Lauris BH <lauris@nix.lv>
2019-10-08 20:54:58 +03:00
Lauris BH 05f6566786 Revert "[Backport] Fix issues/pr list broken when there are man… (#8425)
This reverts commit 0ea4b786cb.
2019-10-08 18:12:41 +02:00
6543 0ea4b786cb [Backport] Fix issues/pr list broken when there are many repositories (#8409) (#8418)
* Fix issues/pr list broken when there are many repositories (#8409)

* fix issues/pr list broken when there are many repositories

* remove unused codes

* fix counting error on issues/prs

* keep the old logic

* fix panic

* fix tests

* rm unused import
2019-10-08 14:35:00 +03:00
6543 30718ce4ac fix bug when migrating a private repository (#7917) (#8403) 2019-10-07 00:58:35 -04:00
6543 aeec78543b Change general form binding to gogs form (#8334) (#8402) 2019-10-07 00:20:07 -04:00
6543 c9b8c12bc9 feat: highlight issue references with : (#8101) (#8404)
* feat: highlight issue references with :

e.g. #1287: my commit msg
e.g. ABC-1234: my commit msg

* ref: update model regex to consistent with issueNumericPattern

* test: check highlight issue with : in commits messages
2019-10-06 23:41:46 -04:00
6543 b0dcf417ea Fix milestone num_issues (#8221) (#8400)
* fix milestone num_issues

* update missing completeness

* only update milestone closed number when closed issue is assigned a new milestone or clear milestone

* fix tests

* fix update milestone num

* fix completeness calculate

* make completeness calucation more clear
2019-10-07 11:03:43 +08:00
6543 797194d2d0 Fix editor commit to new branch if PR disabled (#8375) (#8401) 2019-10-07 09:28:18 +08:00
guillep2k 5bac1a692a Allow users with explicit read access to give approvals (#8398) 2019-10-06 16:52:25 -04:00
6543 b6fba5b459 fix version-validation (because of 1.12 -> 1.13 switch) (#8389)
* fix depenedency

* make vendor first
2019-10-06 00:10:47 -04:00
Elias Norberg e90d88d5d5 Backport of commit status fixes in PR #8316 and PR #8321 to v1.9 (#8339)
* Use correct index when fetching commit status

Signed-off-by: Elias Norberg <elias@aisle.se>

* Compare against base repo to avoid mismatch when merging from fork

Signed-off-by: Elias Norberg <elias@aisle.se>

* Fix pull request commit status in user dashboard list
2019-10-01 19:16:45 +02:00
David Svantesson 7156e2a71a Fix API for edit and delete release attachment (#8290)
* Add logging for when user requested attachment doesn't belong to the specified release.

* Fix API to use correct variable for release asset (attachment)
2019-09-26 21:16:47 +08:00
Daniel Fonseca de Lira e51d91a258 Fix assets on release webhook (#8283) 2019-09-25 22:50:54 +03:00
Daniel Fonseca de Lira 0ed5a647dd Fix release API URL generation (#8239) 2019-09-19 11:15:19 -04:00
guillep2k 3fc42add16 Allow registration when button is hidden (#8238) 2019-09-19 11:58:47 +03:00
Bjoern Petri 4b9786ba62 MS Teams webhook misses commit messages (#8225)
The current webhook just shows the amount of commits, but misses the actual commit description. While the code is actually there to include the description, it is just not included.

Signed-off-by: Bjoern Petri <bjoern.petri@sundevil.de>
2019-09-18 11:51:13 +03:00
Mura Li aaf975bff1 Fix data race (#8206)
* Fix data race

* Fix data race in modules/log

* Make the scope of lock finner-grained

* Use syc.Map

* Fix missing change in the test

* Do not export LoggerMap
2019-09-17 12:40:28 +03:00
Lunny Xiao 267fbbf201 Fix team user api (#8172) (#8188)
* fix team user api

* fix tests

* fix api

* fix team user api

* change user convert

* fix tests

* fix tests
2019-09-17 11:23:40 +08:00
Mura Li 9e8df4b838 Fix pull merge 500 error caused by git-fetch breaking behaviors (#8194) 2019-09-16 13:27:05 -04:00
Jakob Ackermann d78aa189ec [ssh] fix the config specification in the authorized_keys template (#8031) (#8193)
The gitea flags must appear in front of the gitea command. Otherwise
 they are ignored.

Signed-off-by: Jakob Ackermann <das7pad@outlook.com>
(cherry picked from commit 89648746a5)
2019-09-16 09:41:30 +08:00
Filip Navara 6883c007d3 [backport/v1.9] Fix reading git notes from nested trees (#8189)
* Fix reading notes from nested trees

The GIT documentation for notes states "Permitted pathnames have the
form ab/cd/ef/.../abcdef...: a sequence of directory names of two
hexadecimal digits each followed by a filename with the rest of
the object ID."

* Add test case

* Fix new lines
2019-09-15 17:59:08 -04:00
jaqra 91ea086ebe [fix #7384] make show private icon when repo avatar set (#8144) (#8175) 2019-09-14 00:58:31 +03:00
guillep2k b228d22736 Add reviewrs as participants (#8124) 2019-09-12 17:52:33 -04:00
Antoine GIRARD c4d8d53a6d Backport: Use vendored go-swagger (#8087) (#8165)
* Use vendored go-swagger (#8087)

* Use vendored go-swagger

* vendor go-swagger

* revert un wanteed change

* remove un-needed GO111MODULE

* Update Makefile

Co-Authored-By: techknowlogick <matti@mdranta.net>

* re-generate swagger file
2019-09-12 22:58:09 +03:00
Matti R 2f71571305 target go1.12 2019-09-07 12:40:52 -04:00
guillep2k a182a80f7c Changelog for v1.9.3 (#8117)
* Changelog for 1.9.3

* Fix ref to 8071

* Correct punctuation

Co-Authored-By: techknowlogick <matti@mdranta.net>
2019-09-06 12:25:24 -04:00
Rutger Broekhoff 89c57487cd Fix Go 1.13 private repository go get issue (#8100)
* Fix Go 1.13 invalid import path creation

Signed-off-by: Rutger Broekhoff <rutger@viasalix.nl>

* Apply suggested changes from #8100

Signed-off-by: Rutger Broekhoff <rutger@viasalix.nl>
2019-09-06 09:59:03 -04:00
guillep2k bb609cacee Backport: Strict name matching for Repository.GetTagID() (#8082)
* Strict name matching for Repository.GetTagID()

* Add test for GetTagID()
2019-09-04 16:37:09 +02:00
Mura Li e7f6da386f Avoid ambiguity of branch/directory names for the git-diff-tree command (#8066) (#8070) 2019-09-03 22:58:19 +03:00
Mario Lubenka 7727f84fe8 Bugfix/deformed emoji in commit message (backport 1.9) (#8071)
* Fixes deformed emoji in pull request comments or reviews

Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>

* Generate css via command

Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
2019-09-03 18:05:47 +02:00
guillep2k 5d2676089e Add change title notification for issues (#8064) 2019-09-03 11:39:53 +08:00
Tamal Saha 9bea8d825b Run CORS handler first for /api routes (#7967) (#8053)
Signed-off-by: Tamal Saha <tamal@appscode.com>
2019-09-02 08:55:33 +03:00
Mario Lubenka 3cc728870a Evaluate emojis in commit messages in list view (#7906) (#8044)
Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
(cherry picked from commit 08c63190c6)
2019-08-31 21:12:08 +08:00
Lunny Xiao 0793b5e9c0 keep blame view buttons sequence consistent with normal view when view a file (#8007) (#8009) 2019-08-28 08:11:41 +02:00
Richard Mahn bb423f9350 Backport for #7945 (#7994) 2019-08-27 07:01:16 +02:00
Gary Kim 4d6c8d9b13 backport: Fix adding default Telegram webhook (#7992)
Signed-off-by: Gary Kim <gary@garykim.dev>
2019-08-27 12:24:25 +08:00
David Svantesson b6b1560701 Abort syncrhonization from LDAP source if there is some error. (#7965)
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
(cherry picked from commit b2d23a1389)
2019-08-25 02:59:21 +02:00
Lunny Xiao 30dbddcc4d Add release notes of 1.9.2 (#7934)
* Add release notes of 1.9.2

* add missing issue
2019-08-22 16:36:40 +03:00
Mura Li c491c22279 Fix pull creation with empty changes (#7920) (#7926)
* Logs the stderr of git-apply
* Add an integration test
* Skip testPatch when patch is empty
2019-08-20 22:14:09 -04:00
Lunny Xiao 5649f0d2b3 fix wrong sender when send slack webhook (#7918) (#7924) 2019-08-20 13:42:14 -04:00
Lunny Xiao 7dd726faeb upload support text/plain; charset=utf8 (#7899) 2019-08-17 13:59:36 +01:00
Antoine GIRARD 14c979c1b2 drone/docker: prepare multi-arch release + provide arm64 image (#7571) (#7884)
* drone/docker: prepare multi-arch release

* Add docker-linux-arm64 pipeline

* add arm 64 build to manifest

* tag dry-run + indent

* Fix notify dependency
2019-08-15 18:13:24 -04:00
Antoine GIRARD 6b84a1d72b lfs/lock: round locked_at timestamp to second (#7872) (#7875)
* lfs/lock: round locked_at timestamp to second

* test returned locked_at values

* tests: use time RFC3339
2019-08-15 18:53:20 +08:00
Lanre Adelowo 68424eddf0 fix non existent milestone with 500 error (#7867) (#7873) 2019-08-15 09:37:59 +08:00
Lunny Xiao ab23e4b7f4 Add changelog of v1.9.1 (#7859)
* add changelog of v1.9.1

* Update CHANGELOG.md

Co-Authored-By: Lauris BH <lauris@nix.lv>

* mention releases build by go1.12.8

* Update CHANGELOG.md

Co-Authored-By: Lauris BH <lauris@nix.lv>
2019-08-14 16:26:58 +03:00
zeripath 1bb88dad20 Fix local runs of ssh-requiring integration tests (#7857) 2019-08-14 19:02:04 +08:00
zeripath 94f0151789 Attempt to fix hook problem (#7856) 2019-08-14 17:43:02 +08:00
Antoine GIRARD 1e2fe9f0b4 gpg/bugfix: Use .ExpiredUnix.IsZero to display green color of forever valid gpg key (#7846) (#7850)
* Use .ExpiredUnix.IsZero for display green color of gpg key

* remove useless parentheses
2019-08-14 10:59:10 +08:00
Mura Li f3496c88b2 Do not fetch all refs (#7837)
Which would unnecessarily slow down the pull compare operation.
2019-08-13 19:03:01 +08:00
Antoine GIRARD 89915ca8a0 Fix duplicate call of webhook (#7821) (#7824) 2019-08-12 09:53:26 +08:00
Mario Lubenka 24fa56830f Enable switching to a different source branch when PR already exists (#7823)
Signed-off-by: Mario Lubenka <mario.lubenka@googlemail.com>
2019-08-11 23:32:03 +03:00
David Svantesson 0fa9ea516a Rewrite existing repo units if setting is not included in api body (#7811)
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
2019-08-11 12:45:45 +03:00
Gary Kim 9b95b41aa8 Prevent Commit Status and Message From Overflowing On Branch Page (#7800) (#7808)
Signed-off-by: Gary Kim <gary@garykim.dev>
2019-08-09 21:40:16 +03:00
Antoine GIRARD 38e799779f api: fix multiple bugs with statuses endpoints (#7785) (#7807)
* fix commit statuses api url

* search refs before passing sha

* adjust tests

* directly search tags and branches names + remove un-needed check in NewCommitStatus

* fix comment

* de-duplicate code

* test: use relative setting.AppURL

* Update routers/api/v1/repo/status.go

Co-Authored-By: Lauris BH <lauris@nix.lv>

* remove return

* Update routers/api/v1/repo/status.go

Co-Authored-By: Lauris BH <lauris@nix.lv>
2019-08-09 16:18:46 +03:00
WGH 4f39e56795 Fix Slack webhook fork message (#7774) (#7783)
The order of forkee and fork was mixed up.
2019-08-07 11:57:08 -04:00
Lunny Xiao 7b92f91e88 Fix approvals counting (#7757) (#7777)
* fix approvals counting

* fix tests

* fmt
2019-08-07 15:24:01 +08:00
Lunny Xiao aea49d0b92 fix rename failed when rewrite public keys (#7761) (#7769) 2019-08-06 15:41:26 +08:00
zeripath 1b5908fb6a Fix dropTableColumns sqlite implementation (#7710) (#7765)
* Fix dropTableColumns sqlite implementation

* use droptables and its index dropping support in v78 and v85

* golang-ci fixes

* Add migration from gitea 1.3.3 for sqlite which reveals the droptables bug - thus showing this works
2019-08-06 07:35:54 +03:00
zeripath 65a76b7cb0 Be more strict with git arguments (#7715) (#7762)
* Be more strict with git arguments
* fix-up commit test
* use bindings for branch name
2019-08-05 22:05:48 -04:00
Lunny Xiao c6f1825fe9 add pagination for admin api get orgs and fix only list public orgs bug (#7742) (#7752) 2019-08-05 00:08:57 -04:00
guillep2k 4f5dbc4d00 Backport: fix repo_index_status lingering when deleting a repository (#7738) 2019-08-04 10:00:03 +03:00
Lauris BH 4ee8982e91 Fix milestone completness calculation when migrating (#7725) (#7732) 2019-08-04 09:29:17 +08:00
guillep2k 0d10482168 Fixes indexed repos keeping outdated indexes when files grow too large (#7731)
* Fixes indexed repos keeping outdated indexes when files grow too large

Co-Authored-By: zeripath <art27@cantab.net>
2019-08-03 21:42:53 +03:00
guillep2k 6d441de2bd Backport: skip non-regular files (e.g. submodules) on repo indexing (#7717)
* Backport: skip non-regular files (e.g. submodules) on repo indexing

* Include "executable" files in the index, as they are not necessarily binary
2019-08-02 22:40:15 +08:00
Lunny Xiao d15e49f7ff improve branches list performance and fix protected branch icon when no-login (#7695) (#7704) 2019-08-01 11:40:00 -04:00
zeripath 39da4ac6d4 Correct wrong datetime format for git (#7689) (#7690) 2019-07-31 22:13:15 +01:00
techknowlogick 8f29011723 1.9.0 Changelog (#7676) 2019-07-31 09:29:31 -04:00
Lunny Xiao ee43d20d3b fix duplicated webhook when creating issue with assignees (#7681) (#7684) 2019-07-31 14:39:33 +03:00
Lunny Xiao 99ffd826e5 upgrade macaron/captcha to fix random error problem (#7407) (#7683) 2019-07-31 10:09:29 +01:00
David Svantesson 93bac4e10d Backport of #7675. (#7682)
Move add to hook queue for created repo to outside xorm session.

Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
2019-07-31 11:19:14 +03:00
6543 9fbb898058 Show protection symbol if needed on default branch (#7660) (#7668)
* backport issue showing portection symbol if needet at default branch

* sugestion to use range .Branches
https://github.com/go-gitea/gitea/pull/7668/files/d57973a8041a741fdb7e77bc6ab18431bbf95ba4
2019-07-30 22:48:53 +08:00
6543 0a9794a6bc hide delete/restore button on archived repos [backport] (#7660)
* hide delete/restore button on archived repos
close issue #7653
 * backport vor v1.9

* hide column also

* move protected icon to first colum
backport parts of https://github.com/go-gitea/gitea/pull/7461

* backport comit divergenze fix
https://github.com/go-gitea/gitea/issues/7625
2019-07-30 00:20:39 +03:00
Lunny Xiao d4044b9c98 fix bug on migrating milestone from github (#7665) (#7666) 2019-07-29 12:08:24 -04:00
Antoine GIRARD 1e6d2e47e9 css: use flex to fix floating paginate (#7656) (#7662) 2019-07-29 09:05:53 -04:00
Lunny Xiao d827b0bfb7 change length of some repository's columns (#7652) (#7655) 2019-07-29 00:15:13 -04:00
renothing d789170e31 fix wrong email when use gitea as OAuth2 provider (#7640) (#7647)
when you use gitea as OAuth2 provider, the /api/v1/user should return
user primary email as identifier, which is unique in OAuth2 clients.

this patch use convert.ToUser replace all u.APIFormat in api requests,
return primary email when caller is yourself or admin.
2019-07-27 17:25:16 +03:00
Andreas Shimokawa 9bbe3eb0b4 reserve .well-known username (#7638) 2019-07-26 16:37:31 -04:00
silverwind 650fdceb5a Fix syntax highlight initialization (#7617) (#7626)
* Fix syntax highlight initialization

Previously hljs was initialized via a function that relies on the
DOMContentLoaded event, registerd after jQuery's 'ready' event. I assume
that with the recent jQuery update, DOMContentLoaded may not be
guaranteed to fire after 'ready'. Fixed this via vanilla JS initalization.

Fixes: https://github.com/go-gitea/gitea/issues/7559

* semicolon
2019-07-26 07:12:35 +01:00
Lunny Xiao 4c69e158e5 Fix bug create/edit wiki pages when code master branch protected (#7580) (#7623)
* fix bug create/edit wiki pages when code master branch protected

* improve FullPushingEnvironment function
2019-07-26 04:22:20 +03:00
zeripath b7e41f7b8f Fix panic on push at #7611 (#7615) (#7618)
* Fix panic in #7611

Use pr.IssueID instead of pr.Issue.ID as Issue may not be loaded and is unnecessary

* Only fetch the head branch from the remote
2019-07-25 22:23:52 +01:00
zeripath 5a3d9861ba Handle ErrUserProhibitLogin in http git (#7586, #7591) (#7590)
* Handle ErrUserProhibitLogin in http git (#7586)

* Use 403
2019-07-23 17:06:10 -04:00
zeripath adb43358bc fix #7568 (#7587) (#7589) 2019-07-23 20:53:13 +01:00
silverwind d6a980501b Fix file header overflow in file and blame views (#7562) (#7579) 2019-07-23 14:18:39 +08:00
Richard Mahn 103a66ae83 Fixes #7564 - Malformed URLs in API git/commits response (#7565) (#7567) 2019-07-22 22:38:38 +01:00
silverwind 426fd2a816 Fix empty commits now showing in repo overview (#7521) (#7563)
* Fix empty commits now showing in repo overview

* add test

* make fmt
2019-07-22 19:23:49 +03:00
Lunny Xiao 337f2625ac Fix markdown invoke sequence (#7513) (#7560) 2019-07-22 14:27:47 +03:00
Lunny Xiao 5ebf4990a5 Fix repository's pull request count error (#7518) (#7524)
* fix pr count error

* fix tests
2019-07-19 07:40:16 +01:00
Lunny Xiao 3fd07a0be6 remove duplicated webhook trigger (#7511) (#7516) 2019-07-18 21:20:35 +08:00
Antoine GIRARD d372539f79 Backport drone fix from #7480 and #7496 (#7504)
* don't make release-version deps on transalations since translations is only triggered by push on master (#7496)

* drone/docker: Use a more standard format (#7480)

* drone/docker: Use a more standard format

Based on the plugin drone structure itself : https://github.com/drone-plugins/drone-docker/blob/ebce953fc443371d79b5a019fcc9c1976f60a09a/.drone.yml#L9
Use autotag : http://plugins.drone.io/drone-plugins/drone-docker/#autotag

* use latest plugins/docker:linux-amd64

* remove useless cache_from

* Don't depends on translations step
2019-07-18 00:32:29 -04:00
Richard Mahn 91e24a3a10 Fixes #7474 - Handles all redirects for Web UI File CRUD (#7478) (#7507)
* Fixes #7474 - Handles all redirects for Web UI File CRUD

* Fixes lint errors

* Typo fix

* Adds unit tests for a few helper functions

* Fixes per review

* Fix for new branch creation and to unit test

* Fixes the template used for errors on delete
2019-07-18 09:11:55 +08:00
Richard Mahn a29e667eff Backport for #7475 - settings error (#7483) 2019-07-16 12:45:38 +08:00
Matti R 92b993c91f Fetch refs for successful testing for tag (#7388) 2019-07-15 10:16:10 -04:00
techknowlogick 33b1027c76 1.9.0-rc2 changelog (#7467)
* 1.9.0-rc2 changelog

* Update CHANGELOG.md
2019-07-15 17:32:34 +08:00
mrsdizzie b45f9260bf Fix regex for issues in commit messages (#7444) (#7466)
* Fix regex for issues in commit messages

Use same regex as markup for matching in commits.

Fixes #7438

* make fmt
2019-07-14 21:05:59 +01:00
Allen Wild ee1a8d7b41 cmd/serv: actually exit after fatal errors (#7460)
Regression in 356854fc5f, where
a log.Fatal call was removed. log.Fatal calls os.Exit(1) as intended,
but without it the fail() function returns normally and execution
continues past the fatal error, causing a panic.

This is visible as a go panic log and stack trace returned to the SSH
client, which is not only ugly, it leaks server and build system
information.

Fix by removing the stray return statement so that the fail() function
always calls os.Exit(1).

Backport: 2186a99c76
Fixes: https://github.com/go-gitea/gitea/issues/7457
Signed-off-by: Allen Wild <allenwild93@gmail.com>
2019-07-14 12:02:22 +08:00
Gary Kim ba19a35b6b Fix an issue with some pages throwing 'not defined' js exceptions #7450 (#7453)
Fix an issue introduced by cc8e7dd355

Signed-off-by: Gary Kim <gary@garykim.dev>
2019-07-13 22:51:44 +08:00
silverwind cc8e7dd355 fix Dropzone.js integration (#7445) (#7448) 2019-07-12 22:27:36 +01:00
Cherrg f52840623c backport of #7393 - create class for inline positioned lists (#7439)
see #7393
affects #6464

Signed-off-by: Michael Gnehr michael@gnehr.de
2019-07-12 20:11:05 +01:00
silverwind 97d4a38e01 Diff: Fix indentation on unhighlighted code (#7435) (#7443)
There was a missing space before the `nohighlight` class which made the
previous class concatenate wrongly as `wrapnohighlight` in the template.
2019-07-12 12:17:54 -04:00
techknowlogick 60ccd87d6e backport #7425 - jquery 3 (#7442) 2019-07-12 16:20:17 +01:00
Gary Kim 2477737fff Only show "New Pull Request" button if repo allows pulls (#7426) (#7432)
Signed-off-by: Gary Kim <gary@garykim.dev>
2019-07-12 12:35:07 +08:00
Lunny Xiao a360daeff9 Fix vendor (#7394) (#7396) 2019-07-09 04:34:53 -04:00
quantonganh 82d4d725ae only return head: null if source branch was deleted (#6705) (#7376)
* only return head: null if source branch was deleted

* add URL into GetPullRequest

* TestPullRequest_APIFormat

* log error if it is not Err(Branch)NotExist

(cherry picked from commit ff85a6331e)
2019-07-09 10:35:13 +08:00
Cherrg 1e585d7991 backport of #7385 - add missing template variable on organisation settings (#7386)
affects #6755

Signed-off-by: Michael Gnehr <michael@gnehr.de>
2019-07-08 21:03:44 +03:00
Cherrg f849766998 backport of #7380 (#7383)
fix post parameter - on issue list - unset assignee

Signed-off-by: Michael Gnehr <michael@gnehr.de>
2019-07-08 18:50:33 +03:00
Lunny Xiao f4818671e4 Fix migration tests since #7 fixed (#7375) (#7381)
* fix migration tests since #7 fixed

* fix test time
2019-07-08 23:04:46 +08:00
12230 changed files with 1970695 additions and 753353 deletions
-26
View File
@@ -1,26 +0,0 @@
root = "."
tmp_dir = ".air"
[build]
pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs
cmd = "make --no-print-directory backend"
entrypoint = ["./gitea"]
delay = 2000
include_ext = ["go", "tmpl"]
include_file = ["main.go"]
include_dir = ["cmd", "models", "modules", "options", "routers", "services"]
exclude_dir = [
"models/fixtures",
"models/migrations/fixtures",
"modules/avatar/identicon/testdata",
"modules/avatar/testdata",
"modules/git/tests",
"modules/migration/file_format_testdata",
"routers/private/tests",
"services/gitdiff/testdata",
]
exclude_regex = ["_test.go$", "_gen.go$"]
stop_on_error = true
[log]
main_only = true
+28 -43
View File
@@ -1,59 +1,44 @@
# The full repository name
repo: go-gitea/gitea
# Service type (gitea or github)
service: github
# Base URL for Gitea instance if using gitea service type (optional)
# Default: https://gitea.com
base-url:
# Changelog groups and which labeled PRs to add to each group
groups:
-
-
name: BREAKING
labels:
- pr/breaking
-
name: SECURITY
- kind/breaking
-
name: FEATURE
labels:
- topic/security
-
name: FEATURES
labels:
- type/feature
-
name: ENHANCEMENTS
labels:
- type/enhancement
-
name: PERFORMANCE
labels:
- performance/memory
- performance/speed
- performance/bigrepo
- performance/cpu
- kind/feature
-
name: BUGFIXES
labels:
- type/bug
- kind/bug
-
name: ENHANCEMENT
labels:
- kind/enhancement
- kind/refactor
- kind/ui
-
name: SECURITY
labels:
- kind/security
-
name: TESTING
labels:
- type/testing
-
- kind/testing
-
name: TRANSLATION
labels:
- kind/translation
-
name: BUILD
labels:
- topic/build
- topic/code-linting
-
- kind/build
- kind/lint
-
name: DOCS
labels:
- type/docs
-
- kind/docs
-
name: MISC
default: true
# regex indicating which labels to skip for the changelog
skip-labels: skip-changelog|backport\/.+
default: true
-45
View File
@@ -1,45 +0,0 @@
{
"name": "Gitea DevContainer",
"image": "mcr.microsoft.com/devcontainers/go:1.26-trixie",
"containerEnv": {
// override "local" from packaged version
"GOTOOLCHAIN": "auto"
},
"features": {
// installs nodejs into container
"ghcr.io/devcontainers/features/node:1": {
"version": "latest"
},
"ghcr.io/devcontainers/features/git-lfs:1.2.5": {},
"ghcr.io/jsburckhardt/devcontainer-features/uv:1": {},
"ghcr.io/devcontainers/features/python:1": {
"version": "3.14"
},
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
},
"customizations": {
"vscode": {
"settings": {},
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"golang.go",
"stylelint.vscode-stylelint",
"DavidAnson.vscode-markdownlint",
"Vue.volar",
"ms-azuretools.vscode-docker",
"vitest.explorer",
"cweijan.vscode-database-client2",
"GitHub.vscode-pull-request-github",
"Azurite.azurite"
]
}
},
"portsAttributes": {
"3000": {
"label": "Gitea Web",
"onAutoForward": "notify"
}
},
"postCreateCommand": "make deps"
}
-90
View File
@@ -1,90 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# IntelliJ
.idea
# Goland's output filename can not be set manually
/go_build_*
# MS VSCode
.vscode
__debug_bin*
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
*coverage.out
coverage.all
cpu.out
*.db
*.log
/gitea
/debug
/bin
/dist
/custom/*
!/custom/conf
/custom/conf/*
!/custom/conf/app.example.ini
/data
/indexers
/log
/tests/integration/gitea-integration-*
/tests/*.ini
/node_modules
/yarn.lock
/yarn-error.log
/npm-debug.log*
/pnpm-debug.log*
/public/assets/js
/public/assets/css
/public/assets/fonts
/public/assets/img/avatar
/vendor
/VERSION
/.air
/.go-licenses
/Dockerfile
/Dockerfile.rootless
/.venv
# Files and folders that were previously generated
/public/assets/img/webpack
# Snapcraft
snap/.snapcraft/
parts/
stage/
prime/
*.snap
*.snap-build
*_source.tar.bz2
.DS_Store
# Make evidence files
/.make_evidence
# Manpage
/man
+744
View File
@@ -0,0 +1,744 @@
---
kind: pipeline
name: testing
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
services:
- name: mysql
pull: default
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: test
- name: mysql8
pull: default
image: mysql:8.0
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: testgitea
- name: pgsql
pull: default
image: postgres:9.5
environment:
POSTGRES_DB: test
- name: mssql
pull: default
image: microsoft/mssql-server-linux:latest
environment:
ACCEPT_EULA: Y
MSSQL_PID: Standard
SA_PASSWORD: MwantsaSecurePassword1
- name: ldap
pull: default
image: gitea/test-openldap:latest
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
when:
event:
exclude:
- pull_request
- name: pre-build
pull: always
image: webhippie/nodejs:latest
commands:
- make css
- make js
- name: build-without-gcc
pull: always
image: golang:1.10 # this step is kept as the lowest version of golang that we support
commands:
- go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
- name: build
pull: always
image: golang:1.12
commands:
- make clean
- make generate
- make golangci-lint
- make revive
- make swagger-check
- make swagger-validate
- make test-vendor
- make build
environment:
TAGS: bindata sqlite sqlite_unlock_notify
- name: unit-test
pull: always
image: golang:1.12
commands:
- make unit-test-coverage
environment:
TAGS: bindata sqlite sqlite_unlock_notify
depends_on:
- build
when:
branch:
- master
event:
- push
- pull_request
- name: release-test
pull: always
image: golang:1.12
commands:
- make test
environment:
TAGS: bindata sqlite sqlite_unlock_notify
depends_on:
- build
when:
branch:
- "release/*"
event:
- push
- pull_request
- name: tag-pre-condition
pull: always
image: alpine/git
commands:
- git update-ref refs/heads/tag_test ${DRONE_COMMIT_SHA}
depends_on:
- build
when:
event:
- tag
- name: tag-test
pull: always
image: golang:1.12
commands:
- make test
environment:
TAGS: bindata
depends_on:
- tag-pre-condition
when:
event:
- tag
- name: test-sqlite
pull: always
image: golang:1.12
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- timeout -s ABRT 20m make test-sqlite-migration
- timeout -s ABRT 20m make test-sqlite
environment:
TAGS: bindata
depends_on:
- build
- name: test-mysql
pull: always
image: golang:1.12
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- make test-mysql-migration
- make integration-test-coverage
environment:
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
when:
branch:
- master
event:
- push
- pull_request
- name: tag-test-mysql
pull: always
image: golang:1.12
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- timeout -s ABRT 20m make test-mysql-migration
- timeout -s ABRT 20m make test-mysql
environment:
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
when:
event:
- tag
- name: test-mysql8
pull: always
image: golang:1.12
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- timeout -s ABRT 20m make test-mysql8-migration
- timeout -s ABRT 20m make test-mysql8
environment:
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
- name: test-pgsql
pull: always
image: golang:1.12
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- timeout -s ABRT 20m make test-pgsql-migration
- timeout -s ABRT 20m make test-pgsql
environment:
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
- name: test-mssql
pull: always
image: golang:1.12
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- make test-mssql-migration
- make test-mssql
environment:
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
- name: generate-coverage
pull: always
image: golang:1.12
commands:
- make coverage
environment:
TAGS: bindata
depends_on:
- unit-test
- test-mysql
when:
branch:
- master
event:
- push
- pull_request
- name: coverage
pull: always
image: robertstettner/drone-codecov
settings:
files:
- coverage.all
environment:
CODECOV_TOKEN:
from_secret: codecov_token
depends_on:
- generate-coverage
when:
branch:
- master
event:
- push
- pull_request
---
kind: pipeline
name: translations
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
trigger:
branch:
- master
event:
- push
steps:
- name: download
pull: always
image: jonasfranz/crowdin
settings:
download: true
export_dir: options/locale/
ignore_branch: true
project_identifier: gitea
environment:
CROWDIN_KEY:
from_secret: crowdin_key
- name: update
pull: default
image: alpine:3.10
commands:
- mv ./options/locale/locale_en-US.ini ./options/
- "sed -i -e 's/=\"/=/g' -e 's/\"$$//g' ./options/locale/*.ini"
- "sed -i -e 's/\\\\\\\\\"/\"/g' ./options/locale/*.ini"
- mv ./options/locale_en-US.ini ./options/locale/
- name: push
pull: always
image: appleboy/drone-git-push
settings:
author_email: "teabot@gitea.io"
author_name: GiteaBot
commit: true
commit_message: "[skip ci] Updated translations via Crowdin"
remote: "git@github.com:go-gitea/gitea.git"
environment:
GIT_PUSH_SSH_KEY:
from_secret: git_push_ssh_key
- name: upload_translations
pull: always
image: jonasfranz/crowdin
settings:
files:
locale_en-US.ini: options/locale/locale_en-US.ini
ignore_branch: true
project_identifier: gitea
environment:
CROWDIN_KEY:
from_secret: crowdin_key
---
kind: pipeline
name: release-master
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
trigger:
branch:
- master
- "release/*"
event:
- push
depends_on:
- testing
- translations
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
- name: static
pull: always
image: techknowlogick/xgo:go-1.12.x
commands:
- export PATH=$PATH:$GOPATH/bin
- make generate
- make release
environment:
TAGS: bindata sqlite sqlite_unlock_notify
- name: gpg-sign
pull: always
image: plugins/gpgsign:1
settings:
detach_sign: true
excludes:
- "dist/release/*.sha256"
files:
- "dist/release/*"
environment:
GPGSIGN_KEY:
from_secret: gpgsign_key
GPGSIGN_PASSPHRASE:
from_secret: gpgsign_passphrase
depends_on:
- static
- name: release-branch-release
pull: always
image: plugins/s3:1
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
target: "/gitea/${DRONE_BRANCH##release/v}"
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
depends_on:
- gpg-sign
when:
branch:
- "release/*"
event:
- push
- name: release
pull: always
image: plugins/s3:1
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
target: /gitea/master
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
depends_on:
- gpg-sign
when:
branch:
- master
event:
- push
---
kind: pipeline
name: release-version
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
trigger:
event:
- tag
depends_on:
- testing
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
- name: static
pull: always
image: techknowlogick/xgo:go-1.12.x
commands:
- export PATH=$PATH:$GOPATH/bin
- make generate
- make release
environment:
TAGS: bindata sqlite sqlite_unlock_notify
- name: gpg-sign
pull: always
image: plugins/gpgsign:1
settings:
detach_sign: true
excludes:
- "dist/release/*.sha256"
files:
- "dist/release/*"
environment:
GPGSIGN_KEY:
from_secret: gpgsign_key
GPGSIGN_PASSPHRASE:
from_secret: gpgsign_passphrase
depends_on:
- static
- name: release
pull: always
image: plugins/s3:1
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
target: "/gitea/${DRONE_TAG##v}"
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
depends_on:
- gpg-sign
- name: github
pull: always
image: plugins/github-release:1
settings:
files:
- "dist/release/*"
environment:
GITHUB_TOKEN:
from_secret: github_token
depends_on:
- gpg-sign
---
kind: pipeline
name: docs
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
steps:
- name: build-docs
pull: always
image: webhippie/hugo:latest
commands:
- cd docs
- make trans-copy
- make clean
- make build
- name: publish-docs
pull: always
image: lucap/drone-netlify:latest
settings:
path: docs/public/
site_id: d2260bae-7861-4c02-8646-8f6440b12672
environment:
NETLIFY_TOKEN:
from_secret: netlify_token
when:
branch:
- master
event:
- push
---
kind: pipeline
name: docker-linux-amd64
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
depends_on:
- testing
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
- "refs/pull/**"
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
when:
event:
exclude:
- pull_request
- name: dryrun
pull: always
image: plugins/docker:linux-amd64
settings:
dry_run: true
repo: gitea/gitea
tags: linux-amd64
when:
event:
- pull_request
- name: publish
pull: always
image: plugins/docker:linux-amd64
settings:
auto_tag: true
auto_tag_suffix: linux-amd64
repo: gitea/gitea
password:
from_secret: docker_password
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request
---
kind: pipeline
name: docker-linux-arm64
platform:
os: linux
arch: arm64
workspace:
base: /go
path: src/code.gitea.io/gitea
depends_on:
- testing
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
- "refs/pull/**"
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
when:
event:
exclude:
- pull_request
- name: dryrun
pull: always
image: plugins/docker:linux-arm64
settings:
dry_run: true
repo: gitea/gitea
tags: linux-arm64
when:
event:
- pull_request
- name: publish
pull: always
image: plugins/docker:linux-arm64
settings:
auto_tag: true
auto_tag_suffix: linux-arm64
repo: gitea/gitea
password:
from_secret: docker_password
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request
---
kind: pipeline
name: docker-manifest
platform:
os: linux
arch: amd64
steps:
- name: manifest
pull: always
image: plugins/manifest
settings:
auto_tag: true
ignore_missing: true
spec: docker/manifest.tmpl
password:
from_secret: docker_password
username:
from_secret: docker_username
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
depends_on:
- docker-linux-amd64
- docker-linux-arm64
---
kind: pipeline
name: notify
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
when:
status:
- success
- failure
depends_on:
- testing
- translations
- release-version
- release-master
- docker-linux-amd64
- docker-linux-arm64
- docker-manifest
- docs
steps:
- name: discord
pull: always
image: appleboy/drone-discord:1.0.0
environment:
DISCORD_WEBHOOK_ID:
from_secret: discord_webhook_id
DISCORD_WEBHOOK_TOKEN:
from_secret: discord_webhook_token
+20 -26
View File
@@ -1,36 +1,30 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[*.go]
indent_style = tab
indent_size = 8
[*.{tmpl,html}]
indent_style = tab
indent_size = 4
[*.less]
indent_style = space
indent_size = 4
[*.{yml,json}]
indent_style = space
indent_size = 2
tab_width = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{go,tmpl,html}]
indent_style = tab
[go.*]
indent_style = tab
[templates/custom/*.tmpl]
insert_final_newline = false
[templates/swagger/*_json.tmpl]
[*.js]
indent_style = space
insert_final_newline = false
[templates/user/auth/oidc_wellknown.tmpl]
indent_style = space
[templates/shared/actions/runner_badge_*.tmpl]
# editconfig lint requires these XML-like files to have charset defined, but the files don't have.
charset = unset
indent_size = 4
[Makefile]
indent_style = tab
[*.svg]
insert_final_newline = false
-1
View File
@@ -1 +0,0 @@
use flake
+25
View File
@@ -0,0 +1,25 @@
root: true
extends:
- eslint:recommended
parserOptions:
ecmaVersion: 2015
env:
browser: true
jquery: true
es6: true
globals:
Clipboard: false
CodeMirror: false
emojify: false
SimpleMDE: false
Vue: false
Dropzone: false
u2fApi: false
hljs: false
rules:
no-unused-vars: [error, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, ignoreRestSiblings: true}]
+6 -11
View File
@@ -1,11 +1,6 @@
* text=auto eol=lf
*.tmpl linguist-language=Handlebars
*.pb.go linguist-generated
/assets/*.json linguist-generated
/public/assets/img/svg/*.svg linguist-generated
/templates/swagger/v1_json.tmpl linguist-generated
/options/fileicon/** linguist-generated
/vendor/** -text -eol linguist-vendored
/web_src/js/vendor/** -text -eol linguist-vendored
Dockerfile.* linguist-language=Dockerfile
Makefile.* linguist-language=Makefile
conf/* linguist-vendored
docker/* linguist-vendored
options/* linguist-vendored
public/* linguist-vendored
scripts/* linguist-vendored
templates/* linguist-vendored
-91
View File
@@ -1,91 +0,0 @@
name: Bug Report
description: Found something you weren't expecting? Report it here!
labels: ["type/bug"]
body:
- type: markdown
attributes:
value: |
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
- type: markdown
attributes:
value: |
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
3. Make sure you are using the latest release and
take a moment to check that your issue hasn't been reported before.
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)
5. It's really important to provide pertinent details and logs (https://docs.gitea.com/help/support),
incomplete details will be handled as an invalid report.
- type: textarea
id: description
attributes:
label: Description
description: |
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see below)
If you are using a proxy or a CDN (e.g. Cloudflare) in front of Gitea, please disable the proxy/CDN fully and access Gitea directly to confirm the issue still persists without those services.
- type: input
id: gitea-ver
attributes:
label: Gitea Version
description: Gitea version (or commit reference) of your instance
validations:
required: true
- type: dropdown
id: can-reproduce
attributes:
label: Can you reproduce the bug on the Gitea demo site?
description: |
If so, please provide a URL in the Description field
URL of Gitea demo: https://demo.gitea.com
options:
- "Yes"
- "No"
validations:
required: true
- type: markdown
attributes:
value: |
It's really important to provide pertinent logs
Please read https://docs.gitea.com/administration/logging-config#collecting-logs-for-help
In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini
- type: input
id: logs
attributes:
label: Log Gist
description: Please provide a gist URL of your logs, with any sensitive information (e.g. API keys) removed/hidden
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: If this issue involves the Web Interface, please provide one or more screenshots
- type: input
id: git-ver
attributes:
label: Git Version
description: The version of git running on the server
- type: input
id: os-ver
attributes:
label: Operating System
description: The operating system you are using to run Gitea
- type: textarea
id: run-info
attributes:
label: How are you running Gitea?
description: |
Please include information on whether you built Gitea yourself, used one of our downloads, are using https://demo.gitea.com or are using some other package
Please also tell us how you are running Gitea, e.g. if it is being run from docker, a command-line, systemd etc.
If you are using a package or systemd tell us what distribution you are using
validations:
required: true
- type: dropdown
id: database
attributes:
label: Database
description: What database system are you running?
options:
- PostgreSQL
- MySQL/MariaDB
- MSSQL
- SQLite
-17
View File
@@ -1,17 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Security Concern
url: https://tinyurl.com/security-gitea
about: For security concerns, please send a mail to security@gitea.io instead of opening a public issue.
- name: Discord Server
url: https://discord.gg/Gitea
about: Please ask questions and discuss configuration or deployment problems here.
- name: Discourse Forum
url: https://forum.gitea.com
about: Questions and configuration or deployment problems can also be discussed on our forum.
- name: Frequently Asked Questions
url: https://docs.gitea.com/help/faq
about: Please check if your question isn't mentioned here.
- name: Crowdin Translations
url: https://translate.gitea.com
about: Translations are managed here.
@@ -1,24 +0,0 @@
name: Feature Request
description: Got an idea for a feature that Gitea doesn't have currently? Submit your idea here!
labels: ["type/proposal"]
body:
- type: markdown
attributes:
value: |
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
3. Please take a moment to check that your feature hasn't already been suggested.
- type: textarea
id: description
attributes:
label: Feature Description
placeholder: |
I think it would be great if Gitea had...
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: If you can, provide screenshots of an implementation on another site e.g. GitHub
-66
View File
@@ -1,66 +0,0 @@
name: Web Interface Bug Report
description: Something doesn't look quite as it should? Report it here!
labels: ["type/bug", "topic/ui"]
body:
- type: markdown
attributes:
value: |
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
- type: markdown
attributes:
value: |
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
3. Please take a moment to check that your issue doesn't already exist.
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)
5. Please give all relevant information below for bug reports, because
incomplete details will be handled as an invalid report.
6. In particular it's really important to provide pertinent logs. If you are certain that this is a javascript
error, show us the javascript console. If the error appears to relate to Gitea the server you must also give us
DEBUG level logs. (See https://docs.gitea.com/administration/logging-config#collecting-logs-for-help)
- type: textarea
id: description
attributes:
label: Description
description: |
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see below)
If using a proxy or a CDN (e.g. CloudFlare) in front of gitea, please disable the proxy/CDN fully and connect to gitea directly to confirm the issue still persists without those services.
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: Please provide at least 1 screenshot showing the issue.
validations:
required: true
- type: input
id: gitea-ver
attributes:
label: Gitea Version
description: Gitea version (or commit reference) your instance is running
validations:
required: true
- type: dropdown
id: can-reproduce
attributes:
label: Can you reproduce the bug on the Gitea demo site?
description: |
If so, please provide a URL in the Description field
URL of Gitea demo: https://demo.gitea.com
options:
- "Yes"
- "No"
validations:
required: true
- type: input
id: os-ver
attributes:
label: Operating System
description: The operating system you are using to access Gitea
- type: input
id: browser-ver
attributes:
label: Browser Version
description: The browser and version that you are using to access Gitea
validations:
required: true
-7
View File
@@ -1,7 +0,0 @@
self-hosted-runner:
labels:
- actuated-4cpu-8gb
- actuated-4cpu-16gb
- nscloud
- namespace-profile-gitea-release-docker
- namespace-profile-gitea-release-binary
-29
View File
@@ -1,29 +0,0 @@
name: docker-dryrun
description: Composite action that performs the container build steps for a single platform.
inputs:
platform:
description: "The target platform: linux/amd64, linux/arm64, linux/riscv64."
required: true
runs:
using: composite
steps:
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Build regular image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: ${{ inputs.platform }}
push: false
file: Dockerfile
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
- name: Build rootless image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: ${{ inputs.platform }}
push: false
file: Dockerfile.rootless
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless
-47
View File
@@ -1,47 +0,0 @@
name: go-caches
description: Restore and save go module, build, and golangci-lint caches
inputs:
cache-name:
description: Short identifier used in the per-caller build cache key
required: true
build-cache:
description: Whether to include ~/.cache/go-build
default: "true"
build-cache-rotate:
description: Whether to rotate the build cache key per run so Go's test result cache can accumulate across runs
default: "false"
lint-cache:
description: Whether to include ~/.cache/golangci-lint
default: "false"
runs:
using: composite
steps:
- uses: actions/cache@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: ${{ inputs.build-cache == 'true' && inputs.build-cache-rotate == 'true' }}
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/go-build
key: gobuild-${{ runner.os }}-${{ runner.arch }}-${{ inputs.cache-name }}-${{ hashFiles('go.sum') }}-${{ github.run_id }}
restore-keys: |
gobuild-${{ runner.os }}-${{ runner.arch }}-${{ inputs.cache-name }}-${{ hashFiles('go.sum') }}
gobuild-${{ runner.os }}-${{ runner.arch }}-${{ inputs.cache-name }}
gobuild-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
gobuild-${{ runner.os }}-${{ runner.arch }}
- if: ${{ inputs.build-cache == 'true' && inputs.build-cache-rotate != 'true' }}
uses: actions/cache@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' }}
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/golangci-lint
key: golangci-${{ runner.os }}-${{ runner.arch }}-${{ inputs.cache-name }}-${{ hashFiles('go.sum', '.golangci.yml') }}
restore-keys: golangci-${{ runner.os }}-${{ runner.arch }}-${{ inputs.cache-name }}
+33
View File
@@ -0,0 +1,33 @@
<!-- NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue -->
<!--
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
3. Please take a moment to check that your issue doesn't already exist.
4. Please give all relevant information below for bug reports, because
incomplete details will be handled as an invalid report.
-->
- Gitea version (or commit ref):
- Git version:
- Operating system:
- Database (use `[x]`):
- [ ] PostgreSQL
- [ ] MySQL
- [ ] MSSQL
- [ ] SQLite
- Can you reproduce the bug at https://try.gitea.io:
- [ ] Yes (provide example URL)
- [ ] No
- [ ] Not relevant
- Log gist:
## Description
...
## Screenshots
<!-- **If this issue involves the Web Interface, please include a screenshot** -->
-14
View File
@@ -1,14 +0,0 @@
docs-update-needed:
- changed-files:
- any-glob-to-any-file:
- "custom/conf/app.example.ini"
topic/code-linting:
- changed-files:
- any-glob-to-any-file:
- ".golangci.yml"
- ".markdownlint.yaml"
- ".spectral.yaml"
- ".yamllint.yaml"
- "eslint*.config.*"
- "stylelint.config.*"
+6 -10
View File
@@ -1,11 +1,7 @@
<!-- start tips -->
Please check the following:
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for backports.
2. Use a Conventional Commits PR title, for example `fix(repo): handle empty branch names`.
3. Make sure you have read contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md .
4. For documentations contribution, please go to https://gitea.com/gitea/docs
5. Describe what your pull request does and which issue you're targeting (if any).
6. It is recommended to enable "Allow edits by maintainers", so maintainers can help more easily.
7. Your input here will be included in the commit message when this PR has been merged. If you don't want some content to be included, please separate them with a line like `---`.
8. Delete all these tips before posting.
<!-- end tips -->
1. Make sure you are targeting the `master` branch, pull requests on release branches are only allowed for bug fixes.
2. Read contributing guidelines: https://github.com/go-gitea/gitea/blob/master/CONTRIBUTING.md
3. Describe what your pull request does and which issue you're targeting (if any)
**You MUST delete the content above including this line before posting, otherwise your pull request will be invalid.**
+53
View File
@@ -0,0 +1,53 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 60
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 14
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- status/blocked
- kind/security
- lgtm/done
- reviewed/confirmed
- priority/critical
- kind/proposal
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Label to use when marking as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs during the next 2 weeks. Thank you
for your contributions.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This issue has been automatically closed because of inactivity.
You can re-open it if needed.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 1
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
pulls:
daysUntilStale: 60
daysUntilClose: 60
markComment: >
This pull request has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs during the next 2 months. Thank you
for your contributions.
closeComment: >
This pull request has been automatically closed because of inactivity.
You can re-open it if needed.
-73
View File
@@ -1,73 +0,0 @@
# Populates the go module, build, and golangci-lint caches under the default
# branch's cache scope so that PR runs have a warm fallback to restore from.
#
# GitHub Actions caches are scoped per ref: a PR run can only write to its own
# branch's scope, but can read from the base branch's scope as a fallback.
# PRs therefore cannot seed main's scope themselves. Running the same cache
# steps on push-to-main is the only opportunity to populate that fallback
# scope so fresh PR branches start with a useful cache on first run.
# A PR job's exact key lives in its own PR-scope (empty on first run, filled
# by later runs of the same PR); on miss, actions/cache's restore-keys fall
# back to prefix matches against entries this seeder saves in main's scope.
name: cache-seeder
on:
push:
branches:
- main
paths:
- "go.sum"
- ".golangci.yml"
- ".github/actions/go-cache/action.yml"
- ".github/workflows/cache-seeder.yml"
concurrency:
group: cache-seeder
cancel-in-progress: true
permissions:
contents: read
jobs:
gobuild:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: seed
- run: make deps-backend
- run: TAGS="bindata" make backend
- run: TAGS="bindata gogit" GOEXPERIMENT="" make backend
lint:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- { job: lint-backend, tags: "bindata", target: "lint-backend" }
- { job: lint-go-windows, tags: "bindata", target: "lint-go-windows" }
- { job: lint-go-gogit, tags: "bindata gogit", target: "lint-go" }
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: ${{ matrix.job }}
lint-cache: "true"
- run: make deps-backend deps-tools
- run: make ${{ matrix.target }}
env:
TAGS: ${{ matrix.tags }}
-31
View File
@@ -1,31 +0,0 @@
name: cron-licenses
on:
# schedule:
# - cron: "7 0 * * 1" # every Monday at 00:07 UTC
workflow_dispatch:
jobs:
cron-licenses:
runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea'
permissions:
contents: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
- run: make generate-gitignore
timeout-minutes: 40
- name: push translations to repo
uses: appleboy/git-push-action@3b2c8661652360dbf1afe1b319a49dbb739c39f1 # v1.2.0
with:
author_email: "teabot@gitea.io"
author_name: GiteaBot
branch: main
commit: true
commit_message: "[skip ci] Updated licenses and gitignores"
remote: "git@github.com:go-gitea/gitea.git"
ssh_key: ${{ secrets.DEPLOY_KEY }}
-32
View File
@@ -1,32 +0,0 @@
name: cron-renovate
on:
schedule:
- cron: "23 * * * *" # hourly at :23
workflow_dispatch:
concurrency:
group: cron-renovate
env:
RENOVATE_VERSION: 43.141.5 # renovate: datasource=docker depName=ghcr.io/renovatebot/renovate
permissions:
contents: read
jobs:
cron-renovate:
runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea' # prevent running on forks
timeout-minutes: 30
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: renovatebot/github-action@f66d8679fcfcfa051abde6e7a623007173bf5164 # v46.1.12
with:
renovate-version: ${{ env.RENOVATE_VERSION }}
configurationFile: renovate.json5
token: ${{ secrets.RENOVATE_TOKEN }}
env:
RENOVATE_BINARY_SOURCE: install # auto-install go/node toolchains needed by post-upgrade tasks.
RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^make (tidy|svg nolyfill)$"]'
RENOVATE_REPOSITORIES: '["go-gitea/gitea"]'
-40
View File
@@ -1,40 +0,0 @@
name: cron-translations
on:
schedule:
- cron: "7 0 * * *" # every day at 00:07 UTC
workflow_dispatch:
jobs:
crowdin-pull:
runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea'
permissions:
contents: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2
with:
upload_sources: true
upload_translations: false
download_sources: false
download_translations: true
push_translations: false
push_sources: false
create_pull_request: false
config: crowdin.yml
env:
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
- name: update locales
run: ./build/update-locales.sh
- name: push translations to repo
uses: appleboy/git-push-action@3b2c8661652360dbf1afe1b319a49dbb739c39f1 # v1.2.0
with:
author_email: "teabot@gitea.io"
author_name: GiteaBot
branch: main
commit: true
commit_message: "[skip ci] Updated translations via Crowdin"
remote: "git@github.com:go-gitea/gitea.git"
ssh_key: ${{ secrets.DEPLOY_KEY }}
-125
View File
@@ -1,125 +0,0 @@
name: files-changed
on:
workflow_call:
outputs:
backend:
value: ${{ jobs.detect.outputs.backend }}
frontend:
value: ${{ jobs.detect.outputs.frontend }}
docs:
value: ${{ jobs.detect.outputs.docs }}
actions:
value: ${{ jobs.detect.outputs.actions }}
templates:
value: ${{ jobs.detect.outputs.templates }}
docker:
value: ${{ jobs.detect.outputs.docker }}
dockerfile:
value: ${{ jobs.detect.outputs.dockerfile }}
swagger:
value: ${{ jobs.detect.outputs.swagger }}
yaml:
value: ${{ jobs.detect.outputs.yaml }}
json:
value: ${{ jobs.detect.outputs.json }}
e2e:
value: ${{ jobs.detect.outputs.e2e }}
permissions:
contents: read
jobs:
detect:
runs-on: ubuntu-latest
timeout-minutes: 3
outputs:
backend: ${{ steps.changes.outputs.backend }}
frontend: ${{ steps.changes.outputs.frontend }}
docs: ${{ steps.changes.outputs.docs }}
actions: ${{ steps.changes.outputs.actions }}
templates: ${{ steps.changes.outputs.templates }}
docker: ${{ steps.changes.outputs.docker }}
dockerfile: ${{ steps.changes.outputs.dockerfile }}
swagger: ${{ steps.changes.outputs.swagger }}
yaml: ${{ steps.changes.outputs.yaml }}
json: ${{ steps.changes.outputs.json }}
e2e: ${{ steps.changes.outputs.e2e }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changes
with:
filters: |
backend:
- "**/*.go"
- "templates/**/*.tmpl"
- "assets/emoji.json"
- "go.mod"
- "go.sum"
- "Makefile"
- ".golangci.yml"
- ".editorconfig"
- "options/locale/locale_en-US.json"
frontend:
- "*.js"
- "*.ts"
- "web_src/**"
- "tools/*.js"
- "tools/*.ts"
- "assets/emoji.json"
- "package.json"
- "pnpm-lock.yaml"
- "Makefile"
- ".eslintrc.cjs"
- ".npmrc"
docs:
- "**/*.md"
- ".markdownlint.yaml"
- "package.json"
- "pnpm-lock.yaml"
actions:
- ".github/workflows/*"
- "Makefile"
templates:
- "tools/lint-templates-*.js"
- "templates/**/*.tmpl"
- "pyproject.toml"
- "uv.lock"
docker:
- ".github/workflows/pull-docker-dryrun.yml"
- "Dockerfile"
- "Dockerfile.rootless"
- "docker/**"
- "Makefile"
dockerfile:
- "Dockerfile"
- "Dockerfile.rootless"
swagger:
- "templates/swagger/v1_json.tmpl"
- "templates/swagger/v1_input.json"
- "Makefile"
- "package.json"
- "pnpm-lock.yaml"
- ".spectral.yaml"
yaml:
- "**/*.yml"
- "**/*.yaml"
- ".yamllint.yaml"
- "pyproject.toml"
json:
- "**/*.json"
e2e:
- "tests/e2e/**"
- "tools/test-e2e.sh"
- "playwright.config.ts"
-178
View File
@@ -1,178 +0,0 @@
name: compliance
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
files-changed:
uses: ./.github/workflows/files-changed.yml
lint-backend:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: lint-backend
lint-cache: "true"
- run: make deps-backend deps-tools
- run: make lint-backend
env:
TAGS: bindata
lint-on-demand:
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- run: make lint-spell
- if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true'
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true'
run: uv python install 3.14 && make deps-py lint-templates lint-yaml
- if: needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.swagger == 'true' || needs.files-changed.outputs.json == 'true'
run: make deps-frontend lint-md lint-swagger lint-json
- if: needs.files-changed.outputs.actions == 'true'
run: make lint-actions
lint-go-windows:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: lint-go-windows
lint-cache: "true"
- run: make deps-backend deps-tools
- run: make lint-go-windows
env:
TAGS: bindata
GOOS: windows
GOARCH: amd64
lint-go-gogit:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: lint-go-gogit
lint-cache: "true"
- run: make deps-backend deps-tools
- run: make lint-go
env:
TAGS: bindata gogit
checks-backend:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: checks-backend
build-cache: "false"
- run: make deps-backend deps-tools
- run: make --always-make checks-backend # ensure the "go-licenses" make target runs
frontend:
if: needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- run: make deps-frontend
- run: make lint-frontend
- run: make checks-frontend
- run: make test-frontend
- run: make frontend
backend:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: compliance-backend
- run: make deps-backend generate-go
# no frontend build here as backend should be able to build, even without any frontend files
# CGO is not used when cross-compile, so these steps also test if the code is compatible with CGO disabled
- name: build-backend-arm64
run: go build -o gitea_linux_arm64
env:
GOOS: linux
GOARCH: arm64
TAGS: bindata gogit
- name: build-backend-windows
run: go build -o gitea_windows
env:
GOOS: windows
GOARCH: amd64
TAGS: bindata gogit
- name: build-backend-386
run: go build -o gitea_linux_386
env:
GOOS: linux
GOARCH: 386
-262
View File
@@ -1,262 +0,0 @@
name: db-tests
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
files-changed:
uses: ./.github/workflows/files-changed.yml
test-pgsql:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
services:
pgsql:
image: postgres:14
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
ldap:
image: gitea/test-openldap:latest
ports:
- "389:389"
- "636:636"
minio:
# as github actions doesn't support "entrypoint", we need to use a non-official image
# that has a custom entrypoint set to "minio server /data"
image: bitnamilegacy/minio:2023.12.23
env:
MINIO_ROOT_USER: 123456
MINIO_ROOT_PASSWORD: 12345678
ports:
- "9000:9000"
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: pgsql
- name: Add hosts to /etc/hosts
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 pgsql ldap minio" | sudo tee -a /etc/hosts'
- run: make deps-backend
- run: make backend
env:
TAGS: bindata
- name: run migration tests
run: GITEA_TEST_DATABASE=pgsql make test-migration
- name: run tests
run: GITEA_TEST_DATABASE=pgsql make test-integration
timeout-minutes: 50
env:
# pgsql is chosen to be the unlucky one to run with the slow "race detector", it is about 60% slower.
GOTEST_FLAGS: -race -timeout=40m
TAGS: bindata gogit
TEST_LDAP: 1
test-sqlite:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: sqlite
- run: make deps-backend
- run: make backend
env:
TAGS: bindata gogit
GOEXPERIMENT:
- name: run migration tests
run: GITEA_TEST_DATABASE=sqlite make test-migration
env:
TAGS: bindata gogit
- name: run tests
run: GITEA_TEST_DATABASE=sqlite make test-integration
timeout-minutes: 50
env:
# sqlite driver can contain large amount of Golang code, so don't use race detector for it, otherwise, extremely slow
GOTEST_FLAGS: -timeout=40m
TAGS: bindata gogit
GOEXPERIMENT:
test-unit:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.19.14
env:
discovery.type: single-node
xpack.security.enabled: false
ports:
- "9200:9200"
meilisearch:
image: getmeili/meilisearch:v1
env:
MEILI_ENV: development # disable auth
ports:
- "7700:7700"
redis:
image: redis
options: >- # wait until redis has started
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 3s
--health-retries 10
ports:
- 6379:6379
minio:
image: bitnamilegacy/minio:2021.12.29
env:
MINIO_ACCESS_KEY: 123456
MINIO_SECRET_KEY: 12345678
ports:
- "9000:9000"
devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583
image: mcr.microsoft.com/azure-storage/azurite:latest
ports:
- 10000:10000
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: unit
build-cache-rotate: "true"
- name: Add hosts to /etc/hosts
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 minio devstoreaccount1.azurite.local mysql elasticsearch meilisearch smtpimap" | sudo tee -a /etc/hosts'
- run: make deps-backend
- run: make backend
env:
TAGS: bindata
- name: unit-tests
run: make test-backend test-check
env:
GOTEST_FLAGS: -race -timeout=20m
TAGS: bindata
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
- name: unit-tests-gogit
run: make test-backend test-check
env:
GOTEST_FLAGS: -race -timeout=20m
TAGS: bindata gogit
GOEXPERIMENT:
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
test-mysql:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
services:
mysql:
# the bitnami mysql image has more options than the official one, it's easier to customize
image: bitnamilegacy/mysql:8.4
env:
ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: testgitea
ports:
- "3306:3306"
options: >-
--mount type=tmpfs,destination=/bitnami/mysql/data
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.19.14
env:
discovery.type: single-node
xpack.security.enabled: false
ports:
- "9200:9200"
smtpimap:
image: tabascoterrier/docker-imap-devel:latest
ports:
- "25:25"
- "143:143"
- "587:587"
- "993:993"
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: mysql
- name: Add hosts to /etc/hosts
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql elasticsearch smtpimap" | sudo tee -a /etc/hosts'
- run: make deps-backend
- run: make backend
env:
TAGS: bindata
- name: run migration tests
run: GITEA_TEST_DATABASE=mysql make test-migration
- name: run tests
run: GITEA_TEST_DATABASE=mysql make test-integration
env:
TAGS: bindata
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
test-mssql:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
services:
mssql:
image: mcr.microsoft.com/mssql/server:2019-latest
env:
ACCEPT_EULA: Y
MSSQL_PID: Standard
SA_PASSWORD: MwantsaSecurePassword1
ports:
- "1433:1433"
devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583
image: mcr.microsoft.com/azure-storage/azurite:latest
ports:
- 10000:10000
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: mssql
- name: Add hosts to /etc/hosts
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mssql devstoreaccount1.azurite.local" | sudo tee -a /etc/hosts'
- run: make deps-backend
- run: make backend
env:
TAGS: bindata
- run: GITEA_TEST_DATABASE=mssql make test-migration
- name: run tests
run: GITEA_TEST_DATABASE=mssql make test-integration
timeout-minutes: 50
env:
TAGS: bindata
-47
View File
@@ -1,47 +0,0 @@
name: docker-dryrun
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
files-changed:
uses: ./.github/workflows/files-changed.yml
# QEMU-based build is slow (40-50 minutes), so run arm64 and riscv64 when dockerfile changes.
# Run amd64 when any docker-related files change, which is fast (4 minutes).
container-amd64:
if: needs.files-changed.outputs.docker == 'true'
needs: [files-changed]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/docker-dryrun
with:
platform: linux/amd64
container-arm64:
if: needs.files-changed.outputs.dockerfile == 'true'
needs: [files-changed]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/docker-dryrun
with:
platform: linux/arm64
container-riscv64:
if: needs.files-changed.outputs.dockerfile == 'true'
needs: [files-changed]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/docker-dryrun
with:
platform: linux/riscv64
-50
View File
@@ -1,50 +0,0 @@
name: e2e-tests
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
files-changed:
uses: ./.github/workflows/files-changed.yml
test-e2e:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.e2e == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
cache: false
- uses: ./.github/actions/go-cache
with:
cache-name: e2e
build-cache: "false"
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- run: make deps-frontend
- run: make frontend
- run: make deps-backend
- run: make backend
env:
TAGS: bindata
- run: make playwright
- run: make test-e2e
timeout-minutes: 10
env:
TAGS: bindata
FORCE_COLOR: 1
GITEA_TEST_E2E_DEBUG: 1
-20
View File
@@ -1,20 +0,0 @@
name: labeler
on:
pull_request_target:
types: [opened, synchronize, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
labeler:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
with:
sync-labels: true
-28
View File
@@ -1,28 +0,0 @@
name: pr-title
on:
pull_request:
types:
- opened
- edited
- reopened
- synchronize
- ready_for_review
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
lint-pr-title:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- run: make lint-pr-title
env:
PR_TITLE: ${{ github.event.pull_request.title }}
-135
View File
@@ -1,135 +0,0 @@
name: release-nightly
on:
push:
branches: [main, release/v*]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
nightly-binary:
runs-on: namespace-profile-gitea-release-binary
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- 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
env:
TAGS: bindata
- name: import gpg key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0
with:
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
- name: sign binaries
run: |
for f in dist/release/*; do
echo '${{ secrets.GPGSIGN_PASSPHRASE }}' | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u ${{ steps.import_gpg.outputs.fingerprint }} --output "$f.asc" "$f"
done
# clean branch name to get the folder name in S3
- name: Get cleaned branch name
id: clean_name
run: |
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
echo "Cleaned name is ${REF_NAME}"
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
- name: configure aws
uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0
with:
aws-region: ${{ secrets.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: upload binaries to s3
run: |
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
nightly-container:
runs-on: namespace-profile-gitea-release-docker
permissions:
contents: read
packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Get cleaned branch name
id: clean_name
run: |
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
- uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
id: meta
with:
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
tags: |
type=raw,value=${{ steps.clean_name.outputs.branch }}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
id: meta_rootless
with:
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
suffix=-rootless
tags: |
type=raw,value=${{ steps.clean_name.outputs.branch }}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- name: Login to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build regular docker image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
tags: ${{ steps.meta.outputs.tags }}
annotations: ${{ steps.meta.outputs.annotations }}
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
cache-to: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful,mode=max
- name: build rootless docker image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
file: Dockerfile.rootless
tags: ${{ steps.meta_rootless.outputs.tags }}
annotations: ${{ steps.meta_rootless.outputs.annotations }}
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless
cache-to: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless,mode=max
-141
View File
@@ -1,141 +0,0 @@
name: release-tag-rc
on:
push:
tags:
- "v1*-rc*"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
binary:
runs-on: namespace-profile-gitea-release-binary
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- 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
env:
TAGS: bindata
- name: import gpg key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0
with:
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
- name: sign binaries
run: |
for f in dist/release/*; do
echo '${{ secrets.GPGSIGN_PASSPHRASE }}' | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u ${{ steps.import_gpg.outputs.fingerprint }} --output "$f.asc" "$f"
done
# clean branch name to get the folder name in S3
- name: Get cleaned branch name
id: clean_name
run: |
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
echo "Cleaned name is ${REF_NAME}"
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
- name: configure aws
uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0
with:
aws-region: ${{ secrets.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: upload binaries to s3
run: |
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
- name: Install GH CLI
uses: dev-hanz-ops/install-gh-cli-action@af38ce09b1ec248aeb08eea2b16bbecea9e059f8 # v0.2.1
with:
gh-cli-version: 2.39.1
- name: create github release
run: |
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
container:
runs-on: namespace-profile-gitea-release-docker
permissions:
contents: read
packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
id: meta
with:
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
flavor: |
latest=false
# 1.2.3-rc0
tags: |
type=semver,pattern={{version}}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
id: meta_rootless
with:
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
latest=false
suffix=-rootless
# 1.2.3-rc0
tags: |
type=semver,pattern={{version}}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- name: Login to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build regular container image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
tags: ${{ steps.meta.outputs.tags }}
annotations: ${{ steps.meta.outputs.annotations }}
- name: build rootless container image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
file: Dockerfile.rootless
tags: ${{ steps.meta_rootless.outputs.tags }}
annotations: ${{ steps.meta_rootless.outputs.annotations }}
-153
View File
@@ -1,153 +0,0 @@
name: release-tag-version
on:
push:
tags:
- "v1.*"
- "!v1*-rc*"
- "!v1*-dev"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
binary:
runs-on: namespace-profile-gitea-release-binary
permissions:
contents: read
packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
check-latest: true
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
- 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
env:
TAGS: bindata
- name: import gpg key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0
with:
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
- name: sign binaries
run: |
for f in dist/release/*; do
echo '${{ secrets.GPGSIGN_PASSPHRASE }}' | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u ${{ steps.import_gpg.outputs.fingerprint }} --output "$f.asc" "$f"
done
# clean branch name to get the folder name in S3
- name: Get cleaned branch name
id: clean_name
run: |
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
echo "Cleaned name is ${REF_NAME}"
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
- name: configure aws
uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0
with:
aws-region: ${{ secrets.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: upload binaries to s3
run: |
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
- name: Install GH CLI
uses: dev-hanz-ops/install-gh-cli-action@af38ce09b1ec248aeb08eea2b16bbecea9e059f8 # v0.2.1
with:
gh-cli-version: 2.39.1
- name: create github release
run: |
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/*
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
container:
runs-on: namespace-profile-gitea-release-docker
permissions:
contents: read
packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
id: meta
with:
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
# this will generate tags in the following format:
# latest
# 1
# 1.2
# 1.2.3
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
id: meta_rootless
with:
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
suffix=-rootless,onlatest=true
# this will generate tags in the following format (with -rootless suffix added):
# latest
# 1
# 1.2
# 1.2.3
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- name: Login to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build regular container image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
tags: ${{ steps.meta.outputs.tags }}
annotations: ${{ steps.meta.outputs.annotations }}
- name: build rootless container image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
file: Dockerfile.rootless
tags: ${{ steps.meta_rootless.outputs.tags }}
annotations: ${{ steps.meta_rootless.outputs.annotations }}
+25 -68
View File
@@ -9,24 +9,13 @@ _test
# IntelliJ
.idea
.run
# IntelliJ Gateway
.uuid
# Goland's output filename can not be set manually
/go_build_*
/gitea_*
# MS VSCode
.vscode
__debug_bin*
# Visual Studio
/.vs/
# mise version managment tool
mise.toml
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
@@ -39,55 +28,48 @@ _testmain.go
*.exe
*.test
*.prof
*.tsbuildinfo
*coverage.out
coverage.all
cpu.out
/modules/migration/bindata.*
/modules/options/bindata.*
/modules/public/bindata.*
/modules/templates/bindata.*
/modules/options/bindata.go
/modules/public/bindata.go
/modules/templates/bindata.go
*.db
*.log
*.log.*.gz
/gitea
/debug
/integrations.test
/bin
/dist
/custom/*
!/custom/conf/app.example.ini
/custom
/data
/indexers
/log
/public/assets/img/avatar
/tests/e2e-output
/tests/integration/gitea-integration-*
/tests/integration/indexers-*
/tests/*.ini
/tests/**/*.git/**/*.sample
/public/img/avatar
/integrations/gitea-integration-mysql
/integrations/gitea-integration-mysql8
/integrations/gitea-integration-pgsql
/integrations/gitea-integration-sqlite
/integrations/gitea-integration-mssql
/integrations/indexers-mysql
/integrations/indexers-mysql8
/integrations/indexers-pgsql
/integrations/indexers-sqlite
/integrations/indexers-mssql
/integrations/mysql.ini
/integrations/mysql8.ini
/integrations/pgsql.ini
/integrations/mssql.ini
/node_modules
/.venv
/modules/indexer/issues/indexers
routers/repo/authorized_keys
/yarn.lock
/yarn-error.log
/npm-debug.log*
/.pnpm-store
/public/assets/.vite
/public/assets/js
/public/assets/css
/public/assets/fonts
/public/assets/licenses.txt
/vendor
/VERSION
/.air
# Snapcraft
/gitea_a*.txt
snap/.snapcraft/
parts/
stage/
@@ -95,28 +77,3 @@ prime/
*.snap
*.snap-build
*_source.tar.bz2
.DS_Store
# nix-direnv generated files
.direnv/
# Make evidence files
/.make_evidence
# Manpage
/man
# Ignore AI/LLM instruction files
/.claude/
/.cursorrules
/.cursor/
/.goosehints
/.windsurfrules
/.github/copilot-instructions.md
/llms.txt
# Ignore worktrees when working on multiple branches
.worktrees/
# A Makefile for custom make targets
Makefile.local
+91 -190
View File
@@ -1,196 +1,97 @@
version: "2"
output:
sort-order:
- file
linters:
default: none
enable:
- bidichk
- bodyclose
- depguard
- dupl
- errcheck
- forbidigo
- gocheckcompilerdirectives
- gocritic
- goheader
- gosimple
- deadcode
- typecheck
- govet
- ineffassign
- mirror
- modernize
- nakedret
- nilnil
- nolintlint
- perfsprint
- revive
- errcheck
- staticcheck
- testifylint
- unconvert
- unparam
- unused
- usestdlibvars
- usetesting
- wastedassign
settings:
depguard:
rules:
main:
deny:
- pkg: encoding/json
desc: use gitea's modules/json instead of encoding/json
- pkg: github.com/unknwon/com
desc: use gitea's util and replacements
- pkg: io/ioutil
desc: use os or io instead
- pkg: golang.org/x/exp
desc: it's experimental and unreliable
- pkg: code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/git/internal
desc: do not use the internal package, use AddXxx function instead
- pkg: gopkg.in/ini.v1
desc: do not use the ini package, use gitea's config system instead
- pkg: gitea.com/go-chi/cache
desc: do not use the go-chi cache package, use gitea's cache system
- pkg: github.com/pkg/errors
desc: use builtin errors package instead
migrations:
files:
- '**/models/migrations/**/*.go'
deny:
- pkg: code.mokoconsulting.tech/MokoConsulting/MokoGitea/models$
desc: migrations must not depend on the models package
- pkg: code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/structs
desc: migrations must not depend on modules/structs (API structures change over time)
nolintlint:
allow-unused: false
require-explanation: true
require-specific: true
gocritic:
enabled-checks:
- equalFold
disabled-checks:
- ifElseChain
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
- deprecatedComment # conflicts with go-swagger comments
revive:
severity: error
rules:
- name: blank-imports
- name: constant-logical-expr
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: empty-lines
- name: error-return
- name: error-strings
- name: exported
- name: identical-branches
- name: if-return
- name: increment-decrement
- name: modifies-value-receiver
- name: package-comments
- name: redefines-builtin-id
- name: superfluous-else
- name: time-naming
- name: unexported-return
- name: var-declaration
- name: var-naming
arguments:
- [] # AllowList - do not remove as args for the rule are positional and won't work without lists first
- [] # DenyList
- - skip-package-name-checks: true # supress errors from underscore in migration packages
staticcheck:
checks:
- all
- -ST1003
- -ST1005
- -QF1001
- -QF1006
- -QF1008
testifylint:
disable:
- go-require
- require-error
usetesting:
os-temp-dir: true
perfsprint:
concat-loop: false
govet:
enable:
- nilness
- unusedwrite
goheader:
values:
regexp:
HEADER: '((Copyright [^\n]+|All rights reserved\.)\n)*Copyright \d{4} (The (Gogs|Gitea) Authors|Gitea Authors|Gitea)\.( All rights reserved\.)?(\n(Copyright [^\n]+|All rights reserved\.))*\nSPDX-License-Identifier: [\w.-]+'
template: '{{ HEADER }}'
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- dupl
- errcheck
- staticcheck
- unparam
path: _test\.go
- linters:
- dupl
- errcheck
path: models/migrations/v
- linters:
- forbidigo
path: cmd
- linters:
- dupl
text: (?i)webhook
- linters:
- gocritic
text: (?i)`ID' should not be capitalized
- linters:
- unused
text: (?i)swagger
- linters:
- gocritic
text: '(?i)commentFormatting: put a space between `//` and comment text'
- linters:
- gocritic
text: '(?i)exitAfterDefer:'
paths:
- node_modules
- .venv
- public
- web_src
- third_party$
- builtin$
- examples$
issues:
max-issues-per-linter: 0
max-same-issues: 0
formatters:
enable:
- gci
- gofumpt
settings:
gci:
custom-order: true
sections:
- standard
- prefix(code.mokoconsulting.tech/MokoConsulting/MokoGitea)
- blank
- default
gofumpt:
extra-rules: true
exclusions:
generated: lax
paths:
- node_modules
- .venv
- public
- web_src
- structcheck
- varcheck
- golint
- dupl
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
- gofmt
- misspell
- gocritic
enable-all: false
disable-all: true
fast: false
run:
timeout: 10m
linters-settings:
gocritic:
disabled-checks:
- ifElseChain
- singleCaseSwitch # Every time this occured in the code, there was no other way.
issues:
exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- gocyclo
- errcheck
- dupl
- gosec
- unparam
- staticcheck
- path: models/migrations/v
linters:
- gocyclo
- errcheck
- dupl
- gosec
- linters:
- dupl
text: "webhook"
- linters:
- gocritic
text: "`ID' should not be capitalized"
- path: modules/templates/helper.go
linters:
- gocritic
- linters:
- unused
- deadcode
text: "swagger"
- path: contrib/pr/checkout.go
linters:
- errcheck
- path: models/issue.go
linters:
- errcheck
- path: models/migrations/
linters:
- errcheck
- path: modules/log/
linters:
- errcheck
- path: routers/routes/routes.go
linters:
- dupl
- path: routers/repo/view.go
linters:
- dupl
- path: models/migrations/
linters:
- unused
- linters:
- staticcheck
text: "argument x is overwritten before first use"
- path: modules/httplib/httplib.go
linters:
- staticcheck
# Enabling this would require refactoring the methods and how they are called.
- path: models/issue_comment_list.go
linters:
- dupl
# "Destroy" is misspelled in github.com/go-macaron/session/session.go:213 so it's not our responsability to fix it
- path: modules/session/virtual.go
linters:
- misspell
text: '`Destory` is a misspelling of `Destroy`'
- path: modules/session/memory.go
linters:
- misspell
text: '`Destory` is a misspelling of `Destroy`'
-8
View File
@@ -1,8 +0,0 @@
*.min.css
*.min.js
/assets/*.json
/options/gitignore
/options/license
/public/assets
/vendor
node_modules
+3
View File
@@ -0,0 +1,3 @@
pattern = "(?)LGTM"
self_approval_off = true
ignore_maintainers_file = true
-2
View File
@@ -1,2 +0,0 @@
Unknwon <u@gogs.io> <joe2010xtmf@163.com>
Unknwon <u@gogs.io> 无闻 <u@gogs.io>
-15
View File
@@ -1,15 +0,0 @@
commands-show-output: false
fenced-code-language: false
first-line-h1: false
heading-increment: false
line-length: {code_blocks: false, tables: false, stern: true, line_length: -1}
no-alt-text: false
no-bare-urls: false
no-emphasis-as-heading: false
no-empty-links: false
no-hard-tabs: {code_blocks: false}
no-inline-html: false
no-space-in-code: false
no-space-in-emphasis: false
no-trailing-spaces: {br_spaces: 0}
single-h1: false
-25
View File
@@ -1,25 +0,0 @@
{
"mcpServers": {
"ssh": {
"type": "stdio",
"command": "node",
"args": [
"A:/ssh-mcp/src/index.js"
]
},
"wiki": {
"type": "stdio",
"command": "node",
"args": [
"A:/wiki-mcp/dist/index.js"
]
},
"project": {
"type": "stdio",
"command": "node",
"args": [
"A:/project-mcp/dist/index.js"
]
}
}
}
-42
View File
@@ -1,42 +0,0 @@
# MokoGitea
Fork of Gitea -- self-hosted Git service at git.mokoconsulting.tech. Go backend + TypeScript frontend.
## Quick Reference
| Field | Value |
|---|---|
| **Language** | Go 1.26+ / TypeScript |
| **Module** | `code.mokoconsulting.tech/MokoConsulting/MokoGitea` |
| **Branch** | develop on `dev`, merge to `main` (protected) |
| **Wiki** | [MokoGitea Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/wiki) |
## Commands
```bash
make help # List all available targets
make fmt # Format .go files
make lint-go # Lint Go code
make lint-js # Lint TypeScript
make tidy # After go.mod changes
make build # Build binary
# Testing
go test -run '^TestName$' ./modulepath/ # Single Go test
pnpm exec vitest <path-filter> # Single JS test
GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e # Single Playwright test
```
## Rules
- Add current year copyright header on new `.go` files
- No trailing whitespace in edited files
- Conventional Commits for commit messages and PR titles
- Never force-push, amend, or squash unless asked -- use new commits
- Preserve existing code comments
- TypeScript: use `!` (non-null assertion) not `?.`/`??` when value is known to exist
- CSS: prefer `flex-*` helpers over per-child `tw-ml-*`/`tw-mr-*` margins
- Add `Co-Authored-By` lines to all commits
- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
- **Attribution**: `Authored-by: Moko Consulting`
- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform/wiki/Home)
-110
View File
@@ -1,110 +0,0 @@
---
name: Architecture Decision Record (ADR)
about: Propose or document an architectural decision
title: '[ADR] '
labels: 'architecture, decision'
assignees: ''
---
## ADR Number
ADR-XXXX
## Status
- [ ] Proposed
- [ ] Accepted
- [ ] Deprecated
- [ ] Superseded by ADR-XXXX
## Context
Describe the issue or problem that motivates this decision.
## Decision
State the architecture decision and provide rationale.
## Consequences
### Positive
- List positive consequences
### Negative
- List negative consequences or trade-offs
### Neutral
- List neutral aspects
## Alternatives Considered
### Alternative 1
- Description
- Pros
- Cons
- Why not chosen
### Alternative 2
- Description
- Pros
- Cons
- Why not chosen
## Implementation Plan
1. Step 1
2. Step 2
3. Step 3
## Stakeholders
- **Decision Makers**: @user1, @user2
- **Consulted**: @user3, @user4
- **Informed**: team-name
## Technical Details
### Architecture Diagram
```
[Add diagram or link]
```
### Dependencies
- Dependency 1
- Dependency 2
### Impact Analysis
- **Performance**: [Impact description]
- **Security**: [Impact description]
- **Scalability**: [Impact description]
- **Maintainability**: [Impact description]
## Testing Strategy
- [ ] Unit tests
- [ ] Integration tests
- [ ] Performance tests
- [ ] Security tests
## Documentation
- [ ] Architecture documentation updated
- [ ] API documentation updated
- [ ] Developer guide updated
- [ ] Runbook created
## Migration Path
Describe how to migrate from current state to new architecture.
## Rollback Plan
Describe how to rollback if issues occur.
## Timeline
- **Proposal Date**:
- **Decision Date**:
- **Implementation Start**:
- **Expected Completion**:
## References
- Related ADRs:
- External resources:
- RFCs:
## Review Checklist
- [ ] Aligns with enterprise architecture principles
- [ ] Security implications reviewed
- [ ] Performance implications reviewed
- [ ] Cost implications reviewed
- [ ] Compliance requirements met
- [ ] Team consensus achieved
-48
View File
@@ -1,48 +0,0 @@
---
name: Bug Report
about: Report a bug or issue with the project
title: '[BUG] '
labels: 'bug'
assignees: ''
---
## Bug Description
A clear and concise description of what the bug is.
## Steps to Reproduce
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
## Expected Behavior
A clear and concise description of what you expected to happen.
## Actual Behavior
A clear and concise description of what actually happened.
## Screenshots
If applicable, add screenshots to help explain your problem.
## Environment
- **Project**: [e.g., MokoDoliTools, moko-cassiopeia]
- **Version**: [e.g., 1.2.3]
- **Platform**: [e.g., Dolibarr 18.0, Joomla 5.0]
- **PHP Version**: [e.g., 8.1]
- **Database**: [e.g., MySQL 8.0, PostgreSQL 14]
- **Browser** (if applicable): [e.g., Chrome 120, Firefox 121]
- **OS**: [e.g., Ubuntu 22.04, Windows 11]
## Additional Context
Add any other context about the problem here.
## Possible Solution
If you have suggestions on how to fix the issue, please describe them here.
## Checklist
- [ ] I have searched for similar issues before creating this one
- [ ] I have provided all the requested information
- [ ] I have tested this on the latest stable version
- [ ] I have checked the documentation and couldn't find a solution
-18
View File
@@ -1,18 +0,0 @@
---
blank_issues_enabled: true
contact_links:
- name: 💼 Enterprise Support
url: https://mokoconsulting.tech/enterprise
about: Enterprise-level support and consultation services
- name: 💬 Ask a Question
url: https://mokoconsulting.tech/
about: Get help or ask questions through our website
- name: 📚 MokoStandards Documentation
url: https://code.mokoconsulting.tech/MokoConsulting/mokoplatform
about: View our coding standards and best practices
- name: 🔒 Report a Security Vulnerability
url: https://code.mokoconsulting.tech/mokoconsulting-tech/.github-private/security/advisories/new
about: Report security vulnerabilities privately (for critical issues)
- name: 💡 Community Discussions
url: https://github.com/orgs/mokoconsulting-tech/discussions
about: Join community discussions and Q&A
@@ -1,52 +0,0 @@
---
name: Documentation Issue
about: Report an issue with documentation
title: '[DOCS] '
labels: 'documentation'
assignees: ''
---
## Documentation Issue
**Location**:
<!-- Specify the file, page, or section with the issue -->
## Issue Type
<!-- Mark the relevant option with an "x" -->
- [ ] Typo or grammar error
- [ ] Outdated information
- [ ] Missing documentation
- [ ] Unclear explanation
- [ ] Broken links
- [ ] Missing examples
- [ ] Other (specify below)
## Description
<!-- Clearly describe the documentation issue -->
## Current Content
<!-- Quote or describe the current documentation (if applicable) -->
```
Current text here
```
## Suggested Improvement
<!-- Provide your suggestion for how to improve the documentation -->
```
Suggested text here
```
## Additional Context
<!-- Add any other context, screenshots, or references -->
## Standards Alignment
- [ ] Follows MokoStandards documentation guidelines
- [ ] Uses en_US/en_GB localization
- [ ] Includes proper SPDX headers where applicable
## Checklist
- [ ] I have searched for similar documentation issues
- [ ] I have provided a clear description
- [ ] I have suggested an improvement (if applicable)
@@ -1,51 +0,0 @@
---
name: Feature Request
about: Suggest a new feature or enhancement
title: '[FEATURE] '
labels: 'enhancement'
assignees: ''
---
## Feature Description
A clear and concise description of the feature you'd like to see.
## Problem or Use Case
Describe the problem this feature would solve or the use case it addresses.
Ex. I'm always frustrated when [...]
## Proposed Solution
A clear and concise description of what you want to happen.
## Alternative Solutions
A clear and concise description of any alternative solutions or features you've considered.
## Benefits
Describe how this feature would benefit users:
- Who would use this feature?
- What problems does it solve?
- What value does it add?
## Implementation Details (Optional)
If you have ideas about how this could be implemented, share them here:
- Technical approach
- Files/components that might need changes
- Any concerns or challenges you foresee
## Additional Context
Add any other context, mockups, or screenshots about the feature request here.
## Relevant Standards
Does this relate to any standards in [MokoStandards](https://code.mokoconsulting.tech/MokoConsulting/MokoStandards)?
- [ ] Accessibility (WCAG 2.1 AA)
- [ ] Localization (en_US/en_GB)
- [ ] Security best practices
- [ ] Code quality standards
- [ ] Other: [specify]
## Checklist
- [ ] I have searched for similar feature requests before creating this one
- [ ] I have clearly described the use case and benefits
- [ ] I have considered alternative solutions
- [ ] This feature aligns with the project's goals and scope
-82
View File
@@ -1,82 +0,0 @@
---
name: Question
about: Ask a question about usage, features, or best practices
title: '[QUESTION] '
labels: ['question']
assignees: ['jmiller']
---
## Question
**Your question:**
## Context
**What are you trying to accomplish?**
**What have you already tried?**
**Category**:
- [ ] Script usage
- [ ] Configuration
- [ ] Workflow setup
- [ ] Documentation interpretation
- [ ] Best practices
- [ ] Integration
- [ ] Other: __________
## Environment (if relevant)
**Your setup**:
- Operating System:
- Version:
## What You've Researched
**Documentation reviewed**:
- [ ] README.md
- [ ] Project documentation
- [ ] Other (specify): __________
**Similar issues/questions found**:
- #
- #
## Expected Outcome
**What result are you hoping for?**
## Code/Configuration Samples
**Relevant code or configuration** (if applicable):
```bash
# Your code here
```
## Additional Context
**Any other relevant information:**
**Screenshots** (if helpful):
## Urgency
- [ ] Urgent (blocking work)
- [ ] Normal (can work on other things meanwhile)
- [ ] Low priority (just curious)
## Checklist
- [ ] I have searched existing issues and discussions
- [ ] I have reviewed relevant documentation
- [ ] I have provided sufficient context
- [ ] I have included code/configuration samples if relevant
- [ ] This is a genuine question (not a bug report or feature request)
-126
View File
@@ -1,126 +0,0 @@
---
name: Request for Comments (RFC)
about: Propose a significant change for community discussion
title: '[RFC] '
labels: 'rfc, discussion'
assignees: ''
---
## RFC Summary
One-paragraph summary of the proposal.
## Motivation
Why are we doing this? What use cases does it support? What is the expected outcome?
## Detailed Design
### Overview
Provide a detailed explanation of the proposed change.
### API Changes (if applicable)
```php
// Before
function oldApi($param1) { }
// After
function newApi($param1, $param2) { }
```
### User Experience Changes
Describe how users will interact with this change.
### Implementation Approach
High-level implementation strategy.
## Drawbacks
Why should we *not* do this?
## Alternatives
What other designs have been considered? What is the impact of not doing this?
### Alternative 1
- Description
- Trade-offs
### Alternative 2
- Description
- Trade-offs
## Adoption Strategy
How will existing users adopt this? Is this a breaking change?
### Migration Guide
```bash
# Steps to migrate
```
### Deprecation Timeline
- **Announcement**:
- **Deprecation**:
- **Removal**:
## Unresolved Questions
- Question 1
- Question 2
## Future Possibilities
What future work does this enable?
## Impact Assessment
### Performance
Expected performance impact.
### Security
Security considerations and implications.
### Compatibility
- **Backward Compatible**: [Yes / No]
- **Breaking Changes**: [List]
### Maintenance
Long-term maintenance considerations.
## Community Input
### Stakeholders
- [ ] Core team
- [ ] Module developers
- [ ] End users
- [ ] Enterprise customers
### Feedback Period
**Duration**: [e.g., 2 weeks]
**Deadline**: [date]
## Implementation Timeline
### Phase 1: Design
- [ ] RFC discussion
- [ ] Design finalization
- [ ] Approval
### Phase 2: Implementation
- [ ] Core implementation
- [ ] Tests
- [ ] Documentation
### Phase 3: Release
- [ ] Beta release
- [ ] Feedback collection
- [ ] Stable release
## Success Metrics
How will we measure success?
- Metric 1
- Metric 2
## References
- Related RFCs:
- External documentation:
- Prior art:
## Open Questions for Community
1. Question 1?
2. Question 2?
---
**Note**: This RFC is open for community discussion. Please provide feedback in the comments below.
-51
View File
@@ -1,51 +0,0 @@
---
name: Security Vulnerability Report
about: Report a security vulnerability (use only for non-critical issues)
title: '[SECURITY] '
labels: 'security'
assignees: ''
---
## ⚠️ IMPORTANT: Private Disclosure Required
**For critical security vulnerabilities, DO NOT use this template.**
Follow the process in [SECURITY.md](../SECURITY.md) for responsible disclosure.
Use this template only for:
- Security improvements
- Non-critical security suggestions
- Security documentation updates
---
## Security Issue
**Severity**:
<!-- Low, Medium, or informational only -->
## Description
<!-- Describe the security concern or improvement suggestion -->
## Affected Components
<!-- List the affected files, features, or components -->
## Suggested Mitigation
<!-- Describe how this could be addressed -->
## Standards Reference
Does this relate to security standards in [MokoStandards](https://code.mokoconsulting.tech/MokoConsulting/MokoStandards)?
- [ ] SPDX license identifiers
- [ ] Secret management
- [ ] Dependency security
- [ ] Access control
- [ ] Other: [specify]
## Additional Context
<!-- Add any other context about the security concern -->
## Checklist
- [ ] This is NOT a critical vulnerability requiring private disclosure
- [ ] I have reviewed the SECURITY.md policy
- [ ] I have provided sufficient detail for evaluation
@@ -1,9 +0,0 @@
---
name: ".mokogitea Test Template"
about: "Verify .mokogitea issue templates work"
labels: ["test"]
---
This template was loaded from `.mokogitea/ISSUE_TEMPLATE/`.
If you can see this, the `.mokogitea` dot-folder feature is working.
-24
View File
@@ -1,24 +0,0 @@
---
name: Version Bump
about: Request or track a version change
title: '[VERSION] '
labels: 'version, type: version'
assignees: 'jmiller'
---
## Version Change
**Current version**: <!-- e.g., 01.02.03 -->
**Requested version**: <!-- e.g., 01.03.00 -->
**Change type**: <!-- patch / minor / major -->
## Reason
<!-- Why is this version bump needed? -->
## Checklist
- [ ] README.md `VERSION:` field updated
- [ ] CHANGELOG.md entry added
- [ ] Module descriptor version updated (Dolibarr: `$this->version`, Joomla: `<version>`)
- [ ] All file headers will be auto-propagated by `sync-version-on-merge` workflow
-251
View File
@@ -1,251 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokoplatform.Automation
# REPO: https://code.mokoconsulting.tech/MokoConsulting/mokoplatform
# PATH: /.gitea/workflows/branch-protection.yml
# BRIEF: Apply standardised branch protection rules to all governed repositories
#
# +========================================================================+
# | BRANCH PROTECTION SETUP |
# +========================================================================+
# | |
# | Applies protection rules for: main, dev, rc, beta, alpha |
# | |
# | main — Require PR, block rejected reviews, no force push |
# | dev — Allow push, no force push, no delete |
# | rc — Allow push, no force push, no delete |
# | beta — Allow push, no force push, no delete |
# | alpha — Allow push, no force push, no delete |
# | |
# | jmiller has override authority on all branches. |
# | |
# +========================================================================+
name: Branch Protection Setup
on:
schedule:
- cron: '0 2 * * 1' # Weekly Monday 02:00 UTC
workflow_dispatch:
inputs:
dry_run:
description: 'Preview mode (no changes)'
required: false
type: boolean
default: false
repos:
description: 'Comma-separated repo names (empty = all governed repos)'
required: false
type: string
default: ''
env:
GITEA_URL: https://code.mokoconsulting.tech
GITEA_ORG: MokoConsulting
permissions:
contents: read
jobs:
protect:
name: Apply Branch Protection Rules
runs-on: ubuntu-latest
steps:
- name: Determine target repos
id: repos
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: |
API="${GITEA_URL}/api/v1"
# Platform/standards/infra repos to exclude
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private MokoStandards mokoplatform MokoTesting"
EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
if [ -n "${{ inputs.repos }}" ]; then
# User-specified repos
REPOS=$(echo "${{ inputs.repos }}" | tr ',' ' ')
else
# Fetch all org repos
PAGE=1
REPOS=""
while true; do
BATCH=$(curl -sS \
-H "Authorization: token ${GA_TOKEN}" \
"${API}/orgs/${GITEA_ORG}/repos?page=${PAGE}&limit=50" \
| jq -r '.[].name // empty')
[ -z "$BATCH" ] && break
REPOS="$REPOS $BATCH"
PAGE=$((PAGE + 1))
done
# Filter out excluded repos
FILTERED=""
for REPO in $REPOS; do
SKIP=false
for EX in $EXCLUDE; do
if [ "$REPO" = "$EX" ]; then
SKIP=true
break
fi
done
if [ "$SKIP" = "false" ]; then
FILTERED="$FILTERED $REPO"
fi
done
REPOS="$FILTERED"
fi
echo "repos=$REPOS" >> "$GITHUB_OUTPUT"
COUNT=$(echo "$REPOS" | wc -w)
echo "📋 Target repos (${COUNT}): $REPOS"
- name: Apply protection rules
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
DRY_RUN: ${{ inputs.dry_run || 'false' }}
run: |
API="${GITEA_URL}/api/v1"
REPOS="${{ steps.repos.outputs.repos }}"
SUCCESS=0
FAILED=0
SKIPPED=0
# ── Rule definitions ──────────────────────────────────────
# Only the CI bot (jmiller token) can push directly.
# All human contributors must use PRs.
# Force push disabled on all branches.
RULE_MAIN='{
"rule_name": "main",
"enable_push": true,
"enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"],
"enable_force_push": false,
"enable_force_push_allowlist": false,
"force_push_allowlist_usernames": [],
"enable_merge_whitelist": false,
"required_approvals": 0,
"dismiss_stale_approvals": true,
"block_on_rejected_reviews": true,
"block_on_outdated_branch": false,
"priority": 1
}'
RULE_DEV='{
"rule_name": "dev",
"enable_push": true,
"enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"],
"enable_force_push": false,
"enable_force_push_allowlist": false,
"force_push_allowlist_usernames": [],
"enable_merge_whitelist": false,
"required_approvals": 0,
"block_on_rejected_reviews": false,
"priority": 2
}'
RULE_RC='{
"rule_name": "rc",
"enable_push": true,
"enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"],
"enable_force_push": false,
"enable_force_push_allowlist": false,
"force_push_allowlist_usernames": [],
"enable_merge_whitelist": false,
"required_approvals": 0,
"block_on_rejected_reviews": false,
"priority": 3
}'
RULE_BETA='{
"rule_name": "beta",
"enable_push": true,
"enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"],
"enable_force_push": false,
"enable_force_push_allowlist": false,
"force_push_allowlist_usernames": [],
"enable_merge_whitelist": false,
"required_approvals": 0,
"block_on_rejected_reviews": false,
"priority": 4
}'
RULE_ALPHA='{
"rule_name": "alpha",
"enable_push": true,
"enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"],
"enable_force_push": false,
"enable_force_push_allowlist": false,
"force_push_allowlist_usernames": [],
"enable_merge_whitelist": false,
"required_approvals": 0,
"block_on_rejected_reviews": false,
"priority": 5
}'
RULES=("$RULE_MAIN" "$RULE_DEV" "$RULE_RC" "$RULE_BETA" "$RULE_ALPHA")
RULE_NAMES=("main" "dev" "rc" "beta" "alpha")
# ── Apply rules to each repo ──────────────────────────────
for REPO in $REPOS; do
echo ""
echo "═══ ${REPO} ═══"
for i in "${!RULES[@]}"; do
RULE="${RULES[$i]}"
NAME="${RULE_NAMES[$i]}"
if [ "$DRY_RUN" = "true" ]; then
echo " [DRY RUN] Would apply rule: ${NAME}"
SKIPPED=$((SKIPPED + 1))
continue
fi
# Delete existing rule if present (idempotent recreate)
ENCODED_NAME=$(echo "$NAME" | sed 's|/|%2F|g')
curl -sS -o /dev/null -w "" \
-X DELETE \
-H "Authorization: token ${GA_TOKEN}" \
"${API}/repos/${GITEA_ORG}/${REPO}/branch_protections/${ENCODED_NAME}" 2>/dev/null || true
# Create rule
RESPONSE=$(curl -sS -w "\n%{http_code}" \
-X POST \
-H "Authorization: token ${GA_TOKEN}" \
-H "Content-Type: application/json" \
-d "$RULE" \
"${API}/repos/${GITEA_ORG}/${REPO}/branch_protections")
HTTP=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP" = "201" ]; then
echo " ✅ ${NAME}"
SUCCESS=$((SUCCESS + 1))
else
echo " ❌ ${NAME} (HTTP ${HTTP}): $(echo "$BODY" | jq -r '.message // .' 2>/dev/null | head -1)"
FAILED=$((FAILED + 1))
fi
done
done
# ── Summary ───────────────────────────────────────────────
echo ""
echo "════════════════════════════════════════"
echo " ✅ Success: ${SUCCESS}"
echo " ❌ Failed: ${FAILED}"
echo " ⏭️ Skipped: ${SKIPPED}"
echo "════════════════════════════════════════"
if [ "$FAILED" -gt 0 ]; then
echo "::warning::${FAILED} rule(s) failed to apply"
fi
-42
View File
@@ -1,42 +0,0 @@
<!-- NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue -->
<!--
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
3. Please take a moment to check that your issue doesn't already exist.
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)
5. Please give all relevant information below for bug reports, because
incomplete details will be handled as an invalid report.
-->
- Gitea version (or commit ref):
- Git version:
- Operating system:
<!-- Please include information on whether you built gitea yourself, used one of our downloads or are using some other package -->
<!-- Please also tell us how you are running gitea, e.g. if it is being run from docker, a command-line, systemd etc. --->
<!-- If you are using a package or systemd tell us what distribution you are using -->
- Database (use `[x]`):
- [ ] PostgreSQL
- [ ] MySQL
- [ ] MSSQL
- [ ] SQLite
- Can you reproduce the bug at https://demo.gitea.com:
- [ ] Yes (provide example URL)
- [ ] No
- Log gist:
<!-- It really is important to provide pertinent logs -->
<!-- Please read https://docs.gitea.com/administration/logging-config#collecting-logs-for-help -->
<!-- In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini -->
## Description
<!-- If using a proxy or a CDN (e.g. CloudFlare) in front of gitea, please
disable the proxy/CDN fully and connect to gitea directly to confirm
the issue still persists without those services. -->
...
## Screenshots
<!-- **If this issue involves the Web Interface, please include a screenshot** -->
-21
View File
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<mokoplatform xmlns="https://standards.mokoconsulting.tech/mokoplatform/1.0" schema-version="1.0">
<identity>
<name>MokoGitea</name>
<org>MokoConsulting</org>
<description>Moko fork of Gitea - adding project board REST API endpoints and custom enhancements</description>
<version>06.13.00</version>
<version-prefix>v1.26.1+MOKO</version-prefix>
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
</identity>
<governance>
<platform>go</platform>
<standards-version>05.00.00</standards-version>
<standards-source>https://code.mokoconsulting.tech/MokoConsulting/mokoplatform</standards-source>
</governance>
<build>
<language>Go</language>
<package-type>application</package-type>
<entry-point>./</entry-point>
</build>
</mokoplatform>
-129
View File
@@ -1,129 +0,0 @@
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
DEFGROUP: gitea-api-mcp.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp
-->
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Changed
- **Renamed** package from `@mokoconsulting/gitea-api-mcp` to `@mokoconsulting/mokogitea-api-mcp` to distinguish Moko's forked Gitea MCP from upstream
- **Renamed** McpServer name and bin entry to `mokogitea-api-mcp`
## [0.0] - 2026-05-07
### Added
#### User / Auth (3 tools)
- `gitea_me` -- Get the authenticated user info
- `gitea_user_orgs` -- List organizations the authenticated user belongs to
- `gitea_user_repos` -- List repositories owned by the authenticated user
#### Repositories (8 tools)
- `gitea_repo_get` -- Get repository details
- `gitea_repo_create` -- Create a new repository
- `gitea_repo_delete` -- Delete a repository
- `gitea_repo_edit` -- Edit repository settings
- `gitea_repo_fork` -- Fork a repository
- `gitea_repo_search` -- Search repositories
- `gitea_org_repos` -- List repositories in an organization
- `gitea_list_connections` -- List configured Gitea connections
#### File Contents (5 tools)
- `gitea_file_get` -- Get file contents from a repository
- `gitea_dir_get` -- Get directory contents (file listing) from a repository
- `gitea_file_create_or_update` -- Create or update a file in a repository
- `gitea_file_delete` -- Delete a file from a repository
- `gitea_tree_get` -- Get the git tree for a repository (recursive file listing)
#### Branches (4 tools)
- `gitea_branches_list` -- List branches in a repository
- `gitea_branch_get` -- Get a specific branch
- `gitea_branch_create` -- Create a new branch
- `gitea_branch_delete` -- Delete a branch
#### Commits (2 tools)
- `gitea_commits_list` -- List commits in a repository
- `gitea_commit_get` -- Get a specific commit
#### Issues (7 tools)
- `gitea_issues_list` -- List issues in a repository
- `gitea_issue_get` -- Get a single issue by number
- `gitea_issue_create` -- Create a new issue
- `gitea_issue_update` -- Update an issue
- `gitea_issue_comments_list` -- List comments on an issue
- `gitea_issue_comment_create` -- Add a comment to an issue
- `gitea_issue_search` -- Search issues across all repositories
#### Labels (2 tools)
- `gitea_labels_list` -- List labels in a repository
- `gitea_label_create` -- Create a label
#### Milestones (2 tools)
- `gitea_milestones_list` -- List milestones in a repository
- `gitea_milestone_create` -- Create a milestone
#### Pull Requests (6 tools)
- `gitea_pulls_list` -- List pull requests
- `gitea_pull_get` -- Get a single pull request
- `gitea_pull_create` -- Create a pull request
- `gitea_pull_merge` -- Merge a pull request
- `gitea_pull_files` -- List files changed in a pull request
- `gitea_pull_review_create` -- Create a pull request review
#### Releases (5 tools)
- `gitea_releases_list` -- List releases
- `gitea_release_get` -- Get a single release by ID
- `gitea_release_latest` -- Get the latest release
- `gitea_release_create` -- Create a new release
- `gitea_release_delete` -- Delete a release
#### Tags (3 tools)
- `gitea_tags_list` -- List tags
- `gitea_tag_create` -- Create a tag
- `gitea_tag_delete` -- Delete a tag
#### Actions (2 tools)
- `gitea_actions_runs_list` -- List workflow runs for a repository
- `gitea_actions_run_get` -- Get a specific workflow run
#### Organizations (3 tools)
- `gitea_org_get` -- Get organization details
- `gitea_org_teams_list` -- List teams in an organization
- `gitea_org_members_list` -- List members of an organization
#### Users (2 tools)
- `gitea_user_get` -- Get a user profile
- `gitea_users_search` -- Search users
#### Webhooks (2 tools)
- `gitea_webhooks_list` -- List webhooks for a repository
- `gitea_webhook_create` -- Create a webhook
#### Wiki (2 tools)
- `gitea_wiki_pages_list` -- List wiki pages
- `gitea_wiki_page_get` -- Get a wiki page
#### Notifications (2 tools)
- `gitea_notifications_list` -- List notifications for the authenticated user
- `gitea_notifications_read` -- Mark all notifications as read
#### Generic (2 tools)
- `gitea_api_request` -- Make a raw API request to any Gitea v1 endpoint
- `gitea_list_connections` -- List configured Gitea connections
### Infrastructure
- Multi-connection config support via `~/.gitea-api-mcp.json`
- Token-based authentication (Gitea native `Authorization: token` header)
- Built on `node:https` / `node:http` (zero HTTP dependencies)
- MCP SDK v1.12.x with stdio transport
[0.0.1]: https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp/releases/tag/v0.0.1
-18
View File
@@ -1,18 +0,0 @@
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production=false
COPY tsconfig.json ./
COPY src/ ./src/
RUN npx tsc && npm prune --production
EXPOSE 3100
ENV PORT=3100
ENV NODE_ENV=production
# SSE mode by default for Docker deployments
CMD ["node", "dist/sse.js"]
-116
View File
@@ -1,116 +0,0 @@
# MokoGitea MCP Server
A comprehensive [Model Context Protocol](https://modelcontextprotocol.io) server for [Gitea](https://gitea.com) and [MokoGitea](https://git.mokoconsulting.tech/MokoConsulting/MokoGitea). 120+ tools for repos, issues, PRs, projects, releases, custom fields, statuses, priorities, and manifests.
Works with any Gitea instance. MokoGitea-specific features degrade gracefully on vanilla Gitea.
## Quick Start
### npx (no install)
```bash
GITEA_URL=https://gitea.example.com GITEA_TOKEN=your_token npx @mokoconsulting/mokogitea-mcp
```
### Claude Code
Add to `.claude.json`:
```json
{
"mcpServers": {
"mokogitea": {
"command": "npx",
"args": ["@mokoconsulting/mokogitea-mcp"],
"env": {
"GITEA_URL": "https://gitea.example.com",
"GITEA_TOKEN": "your_token"
}
}
}
}
```
### Docker (SSE mode)
```bash
docker run -p 3100:3100 \
-e GITEA_URL=https://gitea.example.com \
-e GITEA_TOKEN=your_token \
mokoconsulting/mokogitea-mcp
```
Connect MCP client to `http://localhost:3100/sse`.
### Multi-instance config
Create `~/.mcp_mokogitea.json`:
```json
{
"defaultConnection": "production",
"connections": {
"production": { "baseUrl": "https://gitea.example.com", "token": "your_token" },
"dev": { "baseUrl": "https://dev.gitea.example.com", "token": "dev_token" }
}
}
```
## Configuration
| Method | Use Case |
|--------|----------|
| `GITEA_URL` + `GITEA_TOKEN` env vars | Single instance, quick setup |
| `~/.mcp_mokogitea.json` config file | Multiple instances |
| `GITEA_API_MCP_CONFIG` env var | Custom config path |
| `GITEA_INSECURE=true` | Skip TLS verification |
## Tools (120+)
### Repositories
`gitea_repo_create` `gitea_repo_get` `gitea_repo_edit` `gitea_repo_delete` `gitea_repo_search` `gitea_repo_fork` `gitea_repo_generate` `gitea_repo_languages` `gitea_repo_contributors` `gitea_repo_topics` `gitea_repo_topics_set`
### Issues
`gitea_issue_create` (dedup by title) `gitea_issue_get` `gitea_issue_update` `gitea_issues_list` `gitea_issue_search` `gitea_issue_comment_create` `gitea_issue_comments_list` `gitea_issue_labels_set` `gitea_issue_bulk_set_status`
### Pull Requests
`gitea_pull_create` `gitea_pull_get` `gitea_pulls_list` `gitea_pull_merge` `gitea_pull_files` `gitea_pull_review_create`
### Branches and Tags
`gitea_branches_list` `gitea_branch_create` `gitea_branch_delete` `gitea_branch_get` `gitea_tags_list` `gitea_tag_create` `gitea_tag_delete`
### Releases
`gitea_releases_list` `gitea_release_create` `gitea_release_get` `gitea_release_latest` `gitea_release_delete` `gitea_release_asset_upload` `gitea_release_asset_delete`
### Files and Trees
`gitea_file_get` `gitea_file_create_or_update` `gitea_file_delete` `gitea_dir_get` `gitea_tree_get` `gitea_bulk_file_push`
### Projects
`gitea_project_list` `gitea_project_create` `gitea_project_get` `gitea_project_update` `gitea_project_delete` `gitea_project_overview` `gitea_project_columns_list` `gitea_project_column_create` `gitea_project_column_delete` `gitea_project_cards_list` `gitea_project_card_add` `gitea_project_card_move` `gitea_project_card_remove`
### Organizations
`gitea_org_get` `gitea_org_repos` `gitea_org_members_list` `gitea_org_teams_list` `gitea_org_labels_list` `gitea_org_label_create`
### Wiki
`gitea_wiki_pages_list` `gitea_wiki_page_get`
### MokoGitea Extensions
`gitea_manifest_get` `gitea_manifest_update` `gitea_org_custom_fields_list` `gitea_org_custom_field_create` `gitea_org_custom_field_delete` `gitea_issue_custom_fields_get` `gitea_issue_custom_fields_set` `gitea_org_issue_statuses_list` `gitea_issue_set_status` `gitea_org_issue_priorities_list` `gitea_issue_set_priority`
### Admin and Other
`gitea_me` `gitea_users_search` `gitea_user_get` `gitea_notifications_list` `gitea_notifications_read` `gitea_commits_list` `gitea_commit_get` `gitea_compare` `gitea_webhooks_list` `gitea_webhook_create` `gitea_admin_users_list` `gitea_admin_orgs_list` `gitea_admin_cron_list` `gitea_admin_cron_run` `gitea_list_connections`
## SSE Server
For hosted deployments:
```
GET / Server info
GET /sse SSE connection endpoint
POST /message Tool call messages
GET /health Health check
```
## License
GPL-3.0-or-later - [Moko Consulting](https://mokoconsulting.tech)
-13
View File
@@ -1,13 +0,0 @@
{
"defaultConnection": "moko",
"connections": {
"moko": {
"baseUrl": "https://git.mokoconsulting.tech",
"token": "your-gitea-access-token"
},
"github-mirror": {
"baseUrl": "https://gitea.example.com",
"token": "your-other-token"
}
}
}
-1198
View File
File diff suppressed because it is too large Load Diff
-58
View File
@@ -1,58 +0,0 @@
{
"name": "@mokoconsulting/mokogitea-mcp",
"version": "1.1.0",
"description": "MCP server for Gitea and MokoGitea - 120+ tools for repos, issues, PRs, projects, releases, custom fields, statuses, priorities, and manifests",
"type": "module",
"main": "dist/index.js",
"bin": {
"mokogitea-mcp": "dist/index.js",
"mokogitea-mcp-sse": "dist/sse.js"
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"start": "node dist/index.js",
"start:sse": "node dist/sse.js",
"setup": "node scripts/setup.mjs",
"clean": "rm -rf dist/"
},
"keywords": [
"mcp",
"gitea",
"mokogitea",
"model-context-protocol",
"claude",
"ai",
"git",
"self-hosted",
"api",
"devops"
],
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.1",
"zod": "^3.24.4"
},
"devDependencies": {
"@types/node": "^22.15.3",
"typescript": "^5.8.3"
},
"engines": {
"node": ">=20.0.0"
},
"license": "GPL-3.0-or-later",
"author": "Moko Consulting <hello@mokoconsulting.tech>",
"homepage": "https://git.mokoconsulting.tech/MokoConsulting/mcp_mokogitea_api",
"repository": {
"type": "git",
"url": "https://git.mokoconsulting.tech/MokoConsulting/mcp_mokogitea_api.git"
},
"files": [
"dist/",
"config.example.json",
"README.md",
"LICENSE"
],
"publishConfig": {
"access": "public"
}
}
-15
View File
@@ -1,15 +0,0 @@
# mcp_mokogitea_api PowerShell Profile
# Source this with: . ./profile.ps1
$env:MCP_ROOT = $PSScriptRoot
$env:TEMP = 'A:\temp'
$env:TMP = 'A:\temp'
function mcp { Set-Location $PSScriptRoot }
function mcp-src { Set-Location (Join-Path $PSScriptRoot 'src') }
function mcp-build { Set-Location $PSScriptRoot; npm run build }
function mcp-dev { Set-Location $PSScriptRoot; npm run dev }
Write-Host "mcp_mokogitea_api profile loaded" -ForegroundColor Cyan
Write-Host " Commands: mcp-build, mcp-dev" -ForegroundColor DarkGray
Write-Host " Navigate: mcp, mcp-src" -ForegroundColor DarkGray
-40
View File
@@ -1,40 +0,0 @@
#!/usr/bin/env node
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* BRIEF: Interactive setup — prompts for Gitea connection details
*/
import { createInterface } from 'node:readline/promises';
import { readFile, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { homedir } from 'node:os';
const CONFIG_PATH = resolve(homedir(), '.gitea-api-mcp.json');
const rl = createInterface({ input: process.stdin, output: process.stdout });
async function prompt(q, d) { const a = await rl.question(`${q}${d ? ` [${d}]` : ''}: `); return a.trim() || d || ''; }
async function promptRequired(q) { let a = ''; while (!a) { a = (await rl.question(`${q}: `)).trim(); if (!a) console.log(' Required.'); } return a; }
async function main() {
console.log('\n=== gitea-api-mcp Setup ===\n');
let existing = null;
try { existing = JSON.parse(await readFile(CONFIG_PATH, 'utf-8')); console.log(`Existing: ${Object.keys(existing.connections).join(', ')}\n`); } catch {}
const name = await prompt('Connection name', 'moko');
const baseUrl = await promptRequired('Gitea URL (e.g. https://git.mokoconsulting.tech)');
const token = await promptRequired('Access token (Settings > Applications > Generate Token)');
const insecure = (await prompt('Skip TLS verification? (y/N)', 'N')).toLowerCase() === 'y';
const conn = { baseUrl: baseUrl.replace(/\/+$/, ''), token };
if (insecure) conn.insecure = true;
const config = existing ?? { defaultConnection: name, connections: {} };
config.connections[name] = conn;
if (!existing) config.defaultConnection = name;
else if ((await prompt(`Set "${name}" as default? (y/N)`, 'N')).toLowerCase() === 'y') config.defaultConnection = name;
await writeFile(CONFIG_PATH, JSON.stringify(config, null, '\t') + '\n', 'utf-8');
console.log(`\nConfig written to ${CONFIG_PATH}\n`);
rl.close();
}
main().catch(e => { console.error(e.message); rl.close(); process.exit(1); });
-120
View File
@@ -1,120 +0,0 @@
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* This file is part of a Moko Consulting project.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: gitea-api-mcp.Client
* INGROUP: gitea-api-mcp
* REPO: https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp
* PATH: /src/client.ts
* VERSION: 01.00.00
* BRIEF: HTTP client for Gitea REST API v1
*/
import * as https from 'node:https';
import * as http from 'node:http';
import type { GiteaConnection, ApiResponse } from './types.js';
const API_PREFIX = '/api/v1';
const TIMEOUT_MS = 30_000;
export class GiteaClient {
private readonly base_url: string;
private readonly headers: Record<string, string>;
private readonly insecure: boolean;
constructor(conn: GiteaConnection) {
this.base_url = conn.baseUrl.replace(/\/+$/, '') + API_PREFIX;
this.headers = {
'Authorization': `token ${conn.token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
};
this.insecure = conn.insecure ?? false;
}
async get(endpoint: string, params?: Record<string, string>): Promise<ApiResponse> {
return this.request(this.buildUrl(endpoint, params), 'GET');
}
async post(endpoint: string, body?: unknown): Promise<ApiResponse> {
return this.request(this.buildUrl(endpoint), 'POST', body);
}
async patch(endpoint: string, body: unknown): Promise<ApiResponse> {
return this.request(this.buildUrl(endpoint), 'PATCH', body);
}
async put(endpoint: string, body: unknown): Promise<ApiResponse> {
return this.request(this.buildUrl(endpoint), 'PUT', body);
}
async delete(endpoint: string): Promise<ApiResponse> {
return this.request(this.buildUrl(endpoint), 'DELETE');
}
private buildUrl(endpoint: string, params?: Record<string, string>): string {
const path = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
const url = new URL(`${this.base_url}${path}`);
if (params) {
for (const [key, value] of Object.entries(params)) {
url.searchParams.set(key, value);
}
}
return url.toString();
}
private request(url: string, method: string, body?: unknown): Promise<ApiResponse> {
return new Promise((resolve, reject) => {
const parsed = new URL(url);
const is_https = parsed.protocol === 'https:';
const transport = is_https ? https : http;
const options: https.RequestOptions = {
hostname: parsed.hostname,
port: parsed.port || (is_https ? 443 : 80),
path: parsed.pathname + parsed.search,
method,
headers: { ...this.headers },
timeout: TIMEOUT_MS,
};
if (this.insecure && is_https) {
options.rejectUnauthorized = false;
}
const payload = body !== undefined ? JSON.stringify(body) : undefined;
if (payload) {
(options.headers as Record<string, string>)['Content-Length'] = Buffer.byteLength(payload).toString();
}
const req = transport.request(options, (res) => {
const chunks: Buffer[] = [];
res.on('data', (chunk: Buffer) => chunks.push(chunk));
res.on('end', () => {
const raw = Buffer.concat(chunks).toString('utf-8');
let data: unknown;
try {
data = JSON.parse(raw);
} catch {
data = raw;
}
resolve({ status: res.statusCode ?? 0, data });
});
});
req.on('error', (err) => reject(err));
req.on('timeout', () => {
req.destroy();
reject(new Error('Request timed out'));
});
if (payload) {
req.write(payload);
}
req.end();
});
}
}
-61
View File
@@ -1,61 +0,0 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
import { readFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { homedir } from 'node:os';
import type { GiteaConfig, GiteaConnection } from './types.js';
const CONFIG_FILENAME = '.mcp_mokogitea.json';
export async function loadConfig(): Promise<GiteaConfig> {
// Priority 1: Environment variables (zero-config single instance)
if (process.env.GITEA_URL && process.env.GITEA_TOKEN) {
const conn: GiteaConnection = {
baseUrl: process.env.GITEA_URL,
token: process.env.GITEA_TOKEN,
insecure: process.env.GITEA_INSECURE === 'true',
};
return {
connections: { default: conn },
defaultConnection: 'default',
};
}
// Priority 2: Config file
const config_path = process.env.GITEA_API_MCP_CONFIG
? resolve(process.env.GITEA_API_MCP_CONFIG)
: resolve(homedir(), CONFIG_FILENAME);
try {
const raw = await readFile(config_path, 'utf-8');
const parsed = JSON.parse(raw) as Partial<GiteaConfig>;
if (!parsed.connections || Object.keys(parsed.connections).length === 0) {
throw new Error('No connections defined in config');
}
return {
connections: parsed.connections,
defaultConnection: parsed.defaultConnection ?? Object.keys(parsed.connections)[0],
};
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new Error(
`Failed to load config from ${config_path}: ${message}\n` +
`Option 1: Set GITEA_URL and GITEA_TOKEN environment variables\n` +
`Option 2: Create ${config_path} - see config.example.json for format`,
);
}
}
export function getConnection(config: GiteaConfig, name?: string): GiteaConnection {
const key = name ?? config.defaultConnection;
const conn = config.connections[key];
if (!conn) {
throw new Error(
`Connection "${key}" not found. Available: ${Object.keys(config.connections).join(', ')}`,
);
}
return conn;
}
File diff suppressed because it is too large Load Diff
-16
View File
@@ -1,16 +0,0 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Creates a configured MCP server instance for use by both stdio and SSE transports.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import type { GiteaConfig } from './types.js';
// Import index.ts to register all tools on its exported `server` singleton,
// then re-export a factory that initializes config and returns the server.
import { server, initConfig } from './index.js';
export function createMcpServer(cfg: GiteaConfig): McpServer {
initConfig(cfg);
return server;
}
-100
View File
@@ -1,100 +0,0 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// SSE transport entry point for MokoGitea MCP server.
// Run with: node dist/sse.js
// Or: GITEA_URL=https://gitea.example.com GITEA_TOKEN=xxx node dist/sse.js
//
// Listens on PORT (default 3100) and serves SSE at /sse with POST at /message.
import { createServer } from 'node:http';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { createMcpServer } from './server.js';
import { loadConfig } from './config.js';
const PORT = parseInt(process.env.PORT ?? '3100', 10);
async function main(): Promise<void> {
const config = await loadConfig();
const transports = new Map<string, SSEServerTransport>();
const httpServer = createServer(async (req, res) => {
// CORS headers for browser clients
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.writeHead(204);
res.end();
return;
}
// Health check
if (req.url === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', tools: 120 }));
return;
}
// SSE endpoint - client connects here
if (req.url === '/sse' && req.method === 'GET') {
const transport = new SSEServerTransport('/message', res);
const sessionId = transport.sessionId;
transports.set(sessionId, transport);
const server = createMcpServer(config);
await server.connect(transport);
req.on('close', () => {
transports.delete(sessionId);
});
return;
}
// Message endpoint - client sends tool calls here
if (req.url?.startsWith('/message') && req.method === 'POST') {
const url = new URL(req.url, `http://${req.headers.host}`);
const sessionId = url.searchParams.get('sessionId');
if (!sessionId || !transports.has(sessionId)) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid or missing sessionId' }));
return;
}
const transport = transports.get(sessionId)!;
await transport.handlePostMessage(req, res);
return;
}
// Root - info page
if (req.url === '/' || req.url === '') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
name: '@mokoconsulting/mokogitea-mcp',
version: '1.1.0',
description: 'MCP server for Gitea and MokoGitea - 120+ tools',
endpoints: {
sse: '/sse',
message: '/message',
health: '/health',
},
docs: 'https://git.mokoconsulting.tech/MokoConsulting/mcp_mokogitea_api',
}));
return;
}
res.writeHead(404);
res.end('Not found');
});
httpServer.listen(PORT, () => {
process.stderr.write(`MokoGitea MCP SSE server listening on port ${PORT}\n`);
process.stderr.write(` SSE: http://localhost:${PORT}/sse\n`);
process.stderr.write(` Health: http://localhost:${PORT}/health\n`);
});
}
main().catch((err) => {
process.stderr.write(`Fatal: ${err}\n`);
process.exit(1);
});
-37
View File
@@ -1,37 +0,0 @@
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* This file is part of a Moko Consulting project.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: gitea-api-mcp.Types
* INGROUP: gitea-api-mcp
* REPO: https://git.mokoconsulting.tech/MokoConsulting/gitea-api-mcp
* PATH: /src/types.ts
* VERSION: 01.00.00
* BRIEF: TypeScript type definitions for Gitea API MCP server
*/
export interface GiteaConnection {
baseUrl: string;
token: string;
/** Skip TLS certificate verification (self-signed certs) */
insecure?: boolean;
}
export interface GitHubBackupConfig {
token: string;
org: string;
}
export interface GiteaConfig {
connections: Record<string, GiteaConnection>;
defaultConnection: string;
github?: GitHubBackupConfig;
}
export interface ApiResponse {
status: number;
data: unknown;
}
-19
View File
@@ -1,19 +0,0 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
-66
View File
@@ -1,66 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.mokogitea/workflows/auto-bump.yml
# VERSION: 09.02.00
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
name: "Universal: Auto Version Bump"
on:
push:
branches:
- dev
- rc
- 'feature/**'
- 'patch/**'
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
permissions:
contents: write
jobs:
bump:
name: Version Bump
runs-on: release
if: >-
!contains(github.event.head_commit.message, '[skip ci]') &&
!contains(github.event.head_commit.message, '[skip bump]') &&
!startsWith(github.event.head_commit.message, 'Merge pull request')
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1
- name: Setup moko-platform tools
run: |
if ! command -v composer &> /dev/null; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
fi
if [ -d "/opt/moko-platform/cli" ]; then
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
else
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
/tmp/moko-platform-api
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
fi
- name: Bump version
run: |
php ${MOKO_CLI}/version_auto_bump.php \
--path . --branch "${GITHUB_REF_NAME}" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
-341
View File
@@ -1,341 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
# PATH: /templates/workflows/universal/auto-release.yml.template
# VERSION: 05.00.00
# BRIEF: Universal build & release detects platform from manifest.xml
#
# +========================================================================+
# | UNIVERSAL BUILD & RELEASE PIPELINE |
# +========================================================================+
# | |
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
# | |
# | Platform-specific: |
# | joomla: XML manifest, type-prefixed packages |
# | dolibarr: mod*.class.php, update.txt, dev version reset |
# | generic: README-only, no update stream |
# | |
# +========================================================================+
name: "Universal: Build & Release"
on:
pull_request:
types: [opened, closed]
branches:
- main
workflow_dispatch:
inputs:
action:
description: 'Action to perform'
required: false
type: choice
default: release
options:
- release
- promote-rc
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
permissions:
contents: write
jobs:
# ── PR Opened → Rename branch to RC and build RC release ─────────────────────
promote-rc:
name: Promote to RC
runs-on: release
if: >-
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
echo Using pre-installed /opt/moko-platform
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/moko-platform-api
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
cd /tmp/moko-platform-api
composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi
- name: Rename branch to rc
run: |
php ${MOKO_CLI}/branch_rename.php \
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
--pr "${{ github.event.pull_request.number }}"
- name: Checkout rc and configure git
run: |
git fetch origin rc
git checkout rc
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Publish RC release
run: |
php ${MOKO_CLI}/release_publish.php \
--path . --stability rc --bump minor --branch rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: Summary
if: always()
run: |
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
release:
name: Build & Release Pipeline
runs-on: release
if: >-
github.event.pull_request.merged == true ||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 0
- name: Configure git for bot pushes
run: |
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Check for merge conflict markers
run: |
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
if [ -n "$CONFLICTS" ]; then
echo "::error::Merge conflict markers found — aborting release"
echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "No conflict markers found"
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
run: |
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
echo Using pre-installed /opt/moko-platform
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/moko-platform-api
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
cd /tmp/moko-platform-api
composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi
- name: "Determine version bump level"
id: bump
run: |
# Fix/patch branches: version was already bumped by pre-release, just strip suffix
# Feature/dev branches: bump minor for the new stable release
HEAD_REF="${{ github.event.pull_request.head.ref || 'dev' }}"
case "$HEAD_REF" in
fix/*|patch/*|hotfix/*|bugfix/*) BUMP="none" ;;
*) BUMP="minor" ;;
esac
echo "level=${BUMP}" >> "$GITHUB_OUTPUT"
echo "Bump level: ${BUMP} (from branch: ${HEAD_REF})"
- name: "Publish stable release"
run: |
BUMP_FLAG=""
if [ "${{ steps.bump.outputs.level }}" != "none" ]; then
BUMP_FLAG="--bump ${{ steps.bump.outputs.level }}"
fi
php ${MOKO_CLI}/release_publish.php \
--path . --stability stable ${BUMP_FLAG} --branch main \
--token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: Update release notes from CHANGELOG.md
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
# Extract [Unreleased] section from changelog
if [ -f "CHANGELOG.md" ]; then
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
[ -z "$NOTES" ] && NOTES="Stable release"
else
NOTES="Stable release"
fi
# Update release body via API
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
"${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ]; then
python3 -c "
import json, urllib.request
body = open('/dev/stdin').read()
payload = json.dumps({'body': body}).encode()
req = urllib.request.Request(
'${API_BASE}/releases/${RELEASE_ID}',
data=payload, method='PATCH',
headers={
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
'Content-Type': 'application/json'
})
urllib.request.urlopen(req)
" <<< "$NOTES"
echo "Release notes updated from CHANGELOG.md"
fi
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub"
if: >-
steps.version.outputs.skip != 'true' &&
secrets.GH_MIRROR_TOKEN != ''
continue-on-error: true
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/release_mirror.php \
--version "$VERSION" --tag "$RELEASE_TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
--branch main 2>&1 || true
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
# -- STEP 10: Sync main branch to GitHub mirror ----------------------------
- name: "Step 10: Push main to GitHub mirror"
if: >-
steps.version.outputs.skip != 'true' &&
secrets.GH_MIRROR_TOKEN != ''
continue-on-error: true
run: |
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1)
GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2)
git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \
git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git"
git fetch origin main --depth=1
git push github origin/main:refs/heads/main --force 2>/dev/null \
&& echo "main branch pushed to GitHub mirror" \
|| echo "WARNING: GitHub mirror push failed"
- name: "Step 11: Delete rc branch and recreate dev from main"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Delete rc branch (ephemeral — created by promote-rc)
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/branches/rc" 2>/dev/null \
&& echo "Deleted rc branch" || echo "rc branch not found"
# Delete dev branch
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch"
# Recreate dev from main (now includes version bump + changelog promotion)
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API_BASE}/branches" \
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY
- name: "Step 12: Create version branch from main"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
BRANCH_NAME="version/${VERSION}"
MAIN_SHA=$(git rev-parse HEAD)
# Delete old version branch if it exists (same version re-release)
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}"
# Create version/XX.YY.ZZ from main
curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed"
echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY
# -- Dolibarr post-release: Reset dev version -----------------------------
- name: "Post-release: Reset dev version"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/version_reset_dev.php \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
--branch dev --path . 2>&1 || true
# -- Summary --------------------------------------------------------------
- name: Pipeline Summary
if: always()
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
PLATFORM="${{ steps.platform.outputs.platform }}"
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
else
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
fi
-48
View File
@@ -1,48 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.mokogitea/workflows/branch-cleanup.yml
# VERSION: 01.00.00
# BRIEF: Delete feature branches after PR merge
name: "Branch Cleanup"
on:
pull_request:
types: [closed]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
cleanup:
name: Delete merged branch
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == true &&
github.event.pull_request.head.ref != 'dev' &&
github.event.pull_request.head.ref != 'main'
steps:
- name: Delete source branch
run: |
BRANCH="${{ github.event.pull_request.head.ref }}"
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
"${API}/${ENCODED}" 2>/dev/null || true)
if [ "$STATUS" = "204" ]; then
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
elif [ "$STATUS" = "404" ]; then
echo "Branch already deleted: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
else
echo "::warning::Failed to delete branch ${BRANCH} (HTTP ${STATUS})"
fi
-10
View File
@@ -1,10 +0,0 @@
# DISABLED — auto-release Step 11 recreates dev from main after every release.
# Cascade-dev is redundant and causes version conflicts when both main and dev
# have different version numbers in templateDetails.xml / manifest.xml.
name: "Cascade Main → Dev (DISABLED)"
on: workflow_dispatch
jobs:
noop:
runs-on: ubuntu-latest
steps:
- run: echo "Cascade disabled — auto-release handles dev recreation"
-204
View File
@@ -1,204 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.gitea/workflows/ci-generic.yml
# VERSION: 01.00.00
# BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js)
name: "Generic: Project CI"
on:
push:
branches:
- main
- dev
- dev/**
- rc/**
- version/**
pull_request:
branches:
- main
- dev
- dev/**
- rc/**
workflow_dispatch:
permissions:
contents: read
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
# ── Lint & Validate ───────────────────────────────────────────────────
lint:
name: Lint & Validate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect toolchain
id: detect
run: |
HAS_PHP=false
HAS_NODE=false
[ -f "composer.json" ] && HAS_PHP=true
[ -f "package.json" ] && HAS_NODE=true
echo "has_php=$HAS_PHP" >> "$GITHUB_OUTPUT"
echo "has_node=$HAS_NODE" >> "$GITHUB_OUTPUT"
echo "Toolchain: PHP=$HAS_PHP Node=$HAS_NODE"
- name: Setup PHP
if: steps.detect.outputs.has_php == 'true'
run: |
if ! command -v php &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
fi
php -v
- name: Setup Node.js
if: steps.detect.outputs.has_node == 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install PHP dependencies
if: steps.detect.outputs.has_php == 'true'
run: |
if [ -f "composer.json" ]; then
composer install --no-interaction --prefer-dist --quiet 2>/dev/null || true
fi
- name: Install Node.js dependencies
if: steps.detect.outputs.has_node == 'true'
run: |
if [ -f "package.json" ]; then
npm ci --quiet 2>/dev/null || npm install --quiet 2>/dev/null || true
fi
- name: PHP syntax check
if: steps.detect.outputs.has_php == 'true'
run: |
ERRORS=0
while IFS= read -r -d '' file; do
if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then
echo "::error file=${file}::PHP syntax error"
ERRORS=$((ERRORS + 1))
fi
done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -not -path "./node_modules/*" -print0)
echo "## PHP Lint" >> $GITHUB_STEP_SUMMARY
if [ "$ERRORS" -eq 0 ]; then
echo "All PHP files passed syntax check." >> $GITHUB_STEP_SUMMARY
else
echo "${ERRORS} file(s) with syntax errors." >> $GITHUB_STEP_SUMMARY
exit 1
fi
- name: TypeScript/JavaScript lint
if: steps.detect.outputs.has_node == 'true'
run: |
if [ -f "node_modules/.bin/eslint" ]; then
npx eslint src/ --quiet 2>&1 || { echo "::error::ESLint errors found"; exit 1; }
echo "## ESLint" >> $GITHUB_STEP_SUMMARY
echo "All files passed ESLint." >> $GITHUB_STEP_SUMMARY
elif [ -f ".eslintrc.json" ] || [ -f ".eslintrc.js" ] || [ -f "eslint.config.js" ]; then
echo "::warning::ESLint config found but eslint not installed"
else
echo "No ESLint configured — skipping"
fi
- name: TypeScript compile check
if: steps.detect.outputs.has_node == 'true'
run: |
if [ -f "tsconfig.json" ] && [ -f "node_modules/.bin/tsc" ]; then
npx tsc --noEmit 2>&1 || { echo "::error::TypeScript compilation errors"; exit 1; }
echo "## TypeScript" >> $GITHUB_STEP_SUMMARY
echo "TypeScript compilation passed." >> $GITHUB_STEP_SUMMARY
fi
- name: PHPStan static analysis
if: steps.detect.outputs.has_php == 'true'
run: |
if [ -f "phpstan.neon" ] && [ -f "vendor/bin/phpstan" ]; then
vendor/bin/phpstan analyse --no-progress 2>&1 || { echo "::warning::PHPStan found issues"; }
fi
# ── Tests ─────────────────────────────────────────────────────────────
test:
name: Tests
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect toolchain
id: detect
run: |
HAS_PHP=false
HAS_NODE=false
[ -f "composer.json" ] && HAS_PHP=true
[ -f "package.json" ] && HAS_NODE=true
echo "has_php=$HAS_PHP" >> "$GITHUB_OUTPUT"
echo "has_node=$HAS_NODE" >> "$GITHUB_OUTPUT"
- name: Setup PHP
if: steps.detect.outputs.has_php == 'true'
run: |
if ! command -v php &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
fi
- name: Setup Node.js
if: steps.detect.outputs.has_node == 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: |
[ -f "composer.json" ] && composer install --no-interaction --prefer-dist --quiet 2>/dev/null || true
[ -f "package.json" ] && { npm ci --quiet 2>/dev/null || npm install --quiet 2>/dev/null || true; }
- name: Run PHP tests
if: steps.detect.outputs.has_php == 'true'
run: |
if [ -f "vendor/bin/phpunit" ]; then
vendor/bin/phpunit --testdox 2>&1
echo "## PHPUnit" >> $GITHUB_STEP_SUMMARY
echo "Tests passed." >> $GITHUB_STEP_SUMMARY
elif [ -f "phpunit.xml" ] || [ -f "phpunit.xml.dist" ]; then
echo "::warning::PHPUnit config found but phpunit not installed"
else
echo "No PHPUnit configured — skipping"
fi
- name: Run Node.js tests
if: steps.detect.outputs.has_node == 'true'
run: |
if jq -e '.scripts.test' package.json > /dev/null 2>&1; then
npm test 2>&1
echo "## Node.js Tests" >> $GITHUB_STEP_SUMMARY
echo "Tests passed." >> $GITHUB_STEP_SUMMARY
else
echo "No test script in package.json — skipping"
fi
- name: Build check
run: |
if [ -f "Makefile" ]; then
make build 2>&1 || echo "::warning::Build failed or not configured"
elif [ -f "package.json" ] && jq -e '.scripts.build' package.json > /dev/null 2>&1; then
npm run build 2>&1 || echo "::warning::Build failed"
fi
-87
View File
@@ -1,87 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Maintenance
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
# PATH: /.gitea/workflows/cleanup.yml
# VERSION: 01.00.00
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
name: "Universal: Repository Cleanup"
on:
schedule:
- cron: '0 3 * * 0' # Weekly on Sunday at 03:00 UTC
workflow_dispatch:
permissions:
contents: write
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs:
cleanup:
name: Clean Merged Branches
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GA_TOKEN }}
- name: Delete merged branches
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: |
echo "=== Merged Branch Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
# List branches via API
BRANCHES=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
"${API}/branches?limit=50" | jq -r '.[].name')
DELETED=0
for BRANCH in $BRANCHES; do
# Skip protected branches
case "$BRANCH" in
main|master|develop|release/*|hotfix/*) continue ;;
esac
# Check if branch is merged into main
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
echo " Deleting merged branch: ${BRANCH}"
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
"${API}/branches/${BRANCH}" 2>/dev/null || true
DELETED=$((DELETED + 1))
fi
done
echo "Deleted ${DELETED} merged branch(es)"
- name: Clean old workflow runs
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: |
echo "=== Workflow Run Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
# Get old completed runs
RUNS=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
"${API}/actions/runs?status=completed&limit=50" | \
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
DELETED=0
for RUN_ID in $RUNS; do
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
DELETED=$((DELETED + 1))
done
echo "Deleted ${DELETED} old workflow run(s)"
-126
View File
@@ -1,126 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Deploy
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
# VERSION: 04.07.00
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
name: "Universal: Deploy to Dev (Manual)"
on:
workflow_dispatch:
inputs:
clear_remote:
description: 'Delete all remote files before uploading'
required: false
default: 'false'
type: boolean
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read
jobs:
deploy:
name: SFTP Deploy to Dev
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup PHP
run: |
php -v && composer --version
- name: Setup MokoStandards tools
env:
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
run: |
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
/tmp/mokostandards-api 2>/dev/null || true
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
fi
- name: Check FTP configuration
id: check
env:
HOST: ${{ vars.DEV_FTP_HOST }}
PATH_VAR: ${{ vars.DEV_FTP_PATH }}
PORT: ${{ vars.DEV_FTP_PORT }}
run: |
if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then
echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "host=$HOST" >> "$GITHUB_OUTPUT"
REMOTE="${PATH_VAR%/}"
echo "remote=$REMOTE" >> "$GITHUB_OUTPUT"
[ -z "$PORT" ] && PORT="22"
echo "port=$PORT" >> "$GITHUB_OUTPUT"
- name: Deploy via SFTP
if: steps.check.outputs.skip != 'true'
env:
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
run: |
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; }
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
"${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \
> /tmp/sftp-config.json
if [ -n "$SFTP_KEY" ]; then
echo "$SFTP_KEY" > /tmp/deploy_key
chmod 600 /tmp/deploy_key
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
else
printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json
fi
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
[ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote)
PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
php /tmp/mokostandards-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
else
php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
fi
rm -f /tmp/deploy_key /tmp/sftp-config.json
- name: Summary
if: always()
run: |
if [ "${{ steps.check.outputs.skip }}" = "true" ]; then
echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY
else
echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY
fi
-273
View File
@@ -1,273 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# BRIEF: Build MokoGitea Docker image, push to registry, and deploy
name: Deploy MokoGitea
on:
push:
branches:
- main
workflow_dispatch:
inputs:
version:
description: 'Version tag (e.g. v1.26.1+MOKO06.12.00)'
required: true
default: 'latest'
environment:
description: 'Target environment'
required: true
default: 'dev'
type: choice
options:
- dev
- production
concurrency:
group: deploy-mokogitea
cancel-in-progress: false
env:
REGISTRY: code.mokoconsulting.tech
IMAGE: mokoconsulting/mokogitea
DEPLOY_HOST: code.mokoconsulting.tech
DEPLOY_PORT: 2918
DEPLOY_USER: mokoconsulting
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout source (for version detection)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine settings
id: config
run: |
# On push to main, auto-deploy to production with git-derived version.
# On workflow_dispatch, use the provided inputs.
if [ "${{ github.event_name }}" = "push" ]; then
VERSION=$(git describe --tags --always 2>/dev/null || echo "dev-$(git rev-parse --short HEAD)")
ENV="production"
else
VERSION="${{ github.event.inputs.version }}"
ENV="${{ github.event.inputs.environment }}"
fi
if [ "$ENV" = "production" ]; then
echo "compose_dir=/opt/gitea" >> $GITHUB_OUTPUT
echo "container=mokogitea" >> $GITHUB_OUTPUT
echo "source_dir=/opt/gitea/source" >> $GITHUB_OUTPUT
echo "branch=main" >> $GITHUB_OUTPUT
echo "tag=${VERSION}" >> $GITHUB_OUTPUT
echo "instance_url=https://code.mokoconsulting.tech" >> $GITHUB_OUTPUT
else
echo "compose_dir=/opt/gitea-dev" >> $GITHUB_OUTPUT
echo "container=mokogitea-dev" >> $GITHUB_OUTPUT
echo "source_dir=/opt/gitea-dev/source" >> $GITHUB_OUTPUT
echo "branch=dev" >> $GITHUB_OUTPUT
echo "tag=${VERSION}-dev" >> $GITHUB_OUTPUT
echo "instance_url=https://git.dev.mokoconsulting.tech" >> $GITHUB_OUTPUT
fi
- name: Enable maintenance mode
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
INSTANCE_URL: ${{ steps.config.outputs.instance_url }}
run: |
echo "Enabling maintenance mode on ${INSTANCE_URL}..."
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/x-www-form-urlencoded" \
"${INSTANCE_URL}/-/admin/config" \
-d 'key=instance.maintenance_mode&value={"AdminWebAccessOnly":true}' \
|| echo "WARNING: Could not enable maintenance mode (instance may be down)"
- name: Build and deploy via SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
TAG: ${{ steps.config.outputs.tag }}
BRANCH: ${{ steps.config.outputs.branch }}
SOURCE_DIR: ${{ steps.config.outputs.source_dir }}
COMPOSE_DIR: ${{ steps.config.outputs.compose_dir }}
CONTAINER: ${{ steps.config.outputs.container }}
run: |
mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
SSH_CMD="ssh -i ~/.ssh/deploy_key -p ${{ env.DEPLOY_PORT }} -o ConnectTimeout=30 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}"
$SSH_CMD "echo 'SSH connected'"
# Pre-deploy cleanup: free disk and memory for the build
$SSH_CMD "
echo 'Cleaning Docker build cache and unused images...'
docker builder prune -af 2>/dev/null || true
docker image prune -af 2>/dev/null || true
echo 'Clearing swap...'
sudo swapoff -a && sudo swapon -a 2>/dev/null || true
echo 'Cleanup complete'
free -m | head -3
"
# Pull latest source
$SSH_CMD "
set -e
if [ ! -d ${SOURCE_DIR}/.git ]; then
git clone -b ${BRANCH} https://code.mokoconsulting.tech/MokoConsulting/MokoGitea.git ${SOURCE_DIR}
fi
cd ${SOURCE_DIR}
git fetch origin ${BRANCH}
git reset --hard origin/${BRANCH}
"
# Build Docker image
$SSH_CMD "
set -e
cd ${SOURCE_DIR}
docker build --no-cache --build-arg GOFLAGS='-p 1' \
--tag ${{ env.REGISTRY }}/${{ env.IMAGE }}:${TAG} \
--tag ${{ env.REGISTRY }}/${{ env.IMAGE }}:latest \
-f Dockerfile .
"
# Push to container registry
$SSH_CMD "
set -e
docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:${TAG}
docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:latest
"
# Update compose and restart
$SSH_CMD "
set -e
cd ${COMPOSE_DIR}
sed -i 's|${{ env.IMAGE }}:[^ ]*|${{ env.IMAGE }}:${TAG}|' docker-compose.yml
docker compose up -d ${CONTAINER}
"
# Health check
$SSH_CMD "
for i in 1 2 3 4 5 6 7 8; do
sleep 15
if docker inspect --format='{{.State.Health.Status}}' ${CONTAINER} 2>/dev/null | grep -q healthy; then
echo 'Container healthy!'
docker inspect --format='Image: {{.Config.Image}}' ${CONTAINER}
exit 0
fi
echo \"Waiting... (attempt \$i/8)\"
done
echo 'Health check failed'
docker logs ${CONTAINER} --tail 20
exit 1
"
- name: Update updates.xml
if: success()
env:
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
TAG: ${{ steps.config.outputs.tag }}
INSTANCE_URL: ${{ steps.config.outputs.instance_url }}
DEPLOY_ENV: ${{ github.event.inputs.environment || 'production' }}
run: |
# Only update updates.xml for production stable releases
if [ "$DEPLOY_ENV" != "production" ]; then
echo "Skipping updates.xml — dev deployments don't update stable channel"
exit 0
fi
# Extract project version by stripping the version prefix from the tag.
# Reads prefix from manifest API (e.g. "v1.26.1+MOKO"), falls back to legacy pattern.
API_BASE="https://${REGISTRY}/api/v1/repos/MokoConsulting/MokoGitea"
PREFIX=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/manifest" | python3 -c "import json,sys; print(json.load(sys.stdin).get('version_prefix',''))" 2>/dev/null || true)
if [ -n "$PREFIX" ]; then
MOKO_VER="${TAG#$PREFIX}"
else
# Legacy fallback: strip everything up to and including "-moko."
MOKO_VER=$(echo "$TAG" | sed -n 's/.*-moko\.\(.*\)/\1/p')
fi
if [ -z "$MOKO_VER" ]; then
echo "Could not extract version from tag: $TAG (prefix: ${PREFIX:-none})"
exit 0
fi
RELEASE_URL="https://${REGISTRY}/MokoConsulting/MokoGitea/releases/tag/${TAG}"
DOCKER_IMG="${REGISTRY}/${IMAGE}:${TAG}"
python3 << PYEOF
import json, os, re, base64, urllib.request
token = os.environ["GITEA_TOKEN"]
registry = os.environ["REGISTRY"]
tag = os.environ["TAG"]
moko_ver = os.environ["MOKO_VER"]
release_url = os.environ["RELEASE_URL"]
docker_img = os.environ["DOCKER_IMG"]
api = f"https://{registry}/api/v1/repos/MokoConsulting/MokoGitea"
# Fetch current updates.xml
req = urllib.request.Request(f"{api}/contents/updates.xml?ref=main",
headers={"Authorization": f"token {token}"})
with urllib.request.urlopen(req) as resp:
data = json.loads(resp.read())
sha = data["sha"]
content = base64.b64decode(data["content"]).decode("utf-8")
# Update stable channel — match the <update> block containing <tag>stable</tag>
def replace_channel(xml, channel, ver, url, docker):
pattern = rf"(<update>\s*<name>MokoGitea</name>[\s\S]*?<tags><tag>{channel}</tag></tags>[\s\S]*?</update>)"
def replacer(m):
block = m.group(1)
block = re.sub(r"<version>[^<]*</version>", f"<version>{ver}</version>", block)
block = re.sub(r"(<infourl[^>]*>)[^<]*(</infourl>)", rf"\1{url}\2", block)
block = re.sub(r"(<downloadurl[^>]*>)[^<]*(</downloadurl>)", rf"\1{docker}\2", block)
return block
return re.sub(pattern, replacer, xml)
content = replace_channel(content, "stable", moko_ver, release_url, docker_img)
content = re.sub(r"VERSION: [^\n]*", f"VERSION: {moko_ver}", content)
# Push updated file
encoded = base64.b64encode(content.encode()).decode()
payload = json.dumps({
"message": f"chore(ci): update updates.xml to {moko_ver}",
"content": encoded,
"sha": sha,
"branch": "main",
}).encode()
req = urllib.request.Request(f"{api}/contents/updates.xml",
data=payload, method="PUT",
headers={"Authorization": f"token {token}", "Content-Type": "application/json"})
with urllib.request.urlopen(req) as resp:
print(f"updates.xml updated to {moko_ver}")
PYEOF
- name: Disable maintenance mode
if: always()
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
INSTANCE_URL: ${{ steps.config.outputs.instance_url }}
run: |
echo "Disabling maintenance mode on ${INSTANCE_URL}..."
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/x-www-form-urlencoded" \
"${INSTANCE_URL}/-/admin/config" \
-d 'key=instance.maintenance_mode&value={"AdminWebAccessOnly":false}' \
|| echo "WARNING: Could not disable maintenance mode"
- name: Verify
run: |
sleep 5
curl -sf https://${{ env.DEPLOY_HOST }}/api/healthz && echo " — API healthy"
- name: Notify on failure
if: failure()
run: echo "::error::Deploy failed for ${{ steps.config.outputs.tag }}"
-96
View File
@@ -1,96 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Security
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
# PATH: /templates/workflows/gitleaks.yml.template
# VERSION: 01.00.00
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
#
# +========================================================================+
# | SECRET SCANNING |
# +========================================================================+
# | |
# | Scans commits for leaked secrets using Gitleaks. |
# | |
# | - PR scan: only new commits in the PR |
# | - Scheduled: full repo scan weekly |
# | - Alerts via ntfy on findings |
# | |
# +========================================================================+
name: "Universal: Secret Scanning"
on:
pull_request:
branches:
- main
- 'dev/**'
schedule:
- cron: '0 5 * * 1' # Weekly Monday 05:00 UTC
workflow_dispatch:
permissions:
contents: read
env:
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }}
jobs:
gitleaks:
name: Gitleaks Secret Scan
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Gitleaks
run: |
GITLEAKS_VERSION="8.21.2"
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
| tar -xz -C /usr/local/bin gitleaks
gitleaks version
- name: Scan for secrets
id: scan
run: |
echo "### Secret Scanning" >> $GITHUB_STEP_SUMMARY
ARGS="--source . --verbose --report-format json --report-path /tmp/gitleaks-report.json"
if [ "${{ github.event_name }}" = "pull_request" ]; then
# Scan only PR commits
ARGS="$ARGS --log-opts=${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}"
echo "Scanning PR commits only" >> $GITHUB_STEP_SUMMARY
else
echo "Full repository scan" >> $GITHUB_STEP_SUMMARY
fi
if gitleaks detect $ARGS 2>&1; then
echo "result=clean" >> "$GITHUB_OUTPUT"
echo "**No secrets detected.**" >> $GITHUB_STEP_SUMMARY
else
echo "result=found" >> "$GITHUB_OUTPUT"
FINDINGS=$(jq length /tmp/gitleaks-report.json 2>/dev/null || echo "unknown")
echo "**${FINDINGS} potential secret(s) detected.**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Review the findings and rotate any exposed credentials immediately." >> $GITHUB_STEP_SUMMARY
exit 1
fi
- name: Notify on findings
if: failure() && steps.scan.outputs.result == 'found'
run: |
REPO="${{ github.event.repository.name }}"
curl -sS \
-H "Title: ${REPO} — secrets detected in code" \
-H "Tags: rotating_light,key" \
-H "Priority: urgent" \
-d "Gitleaks found potential secrets. Review and rotate credentials immediately." \
"${NTFY_URL}/${NTFY_TOPIC}" || true
-73
View File
@@ -1,73 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokoplatform.Automation
# VERSION: 06.13.00
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
on:
issues:
types: [opened]
permissions:
contents: write
issues: write
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://code.mokoconsulting.tech' }}
jobs:
create-branch:
name: Create feature branch
runs-on: ubuntu-latest
steps:
- name: Create branch and comment
run: |
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
ISSUE_NUM="${{ github.event.issue.number }}"
ISSUE_TITLE="${{ github.event.issue.title }}"
# Build slug from title: lowercase, replace non-alnum with dash, trim
SLUG=$(echo "${ISSUE_TITLE}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | cut -c1-40)
BRANCH="feature/${ISSUE_NUM}-${SLUG}"
# Check dev branch exists
DEV_EXISTS=$(curl -sf -o /dev/null -w '%{http_code}' \
-H "Authorization: token ${TOKEN}" \
"${API}/branches/dev" 2>/dev/null || echo "000")
if [ "${DEV_EXISTS}" != "200" ]; then
echo "No dev branch -- skipping"
exit 0
fi
# Create branch from dev
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API}/branches" \
-d "{\"new_branch_name\":\"${BRANCH}\",\"old_branch_name\":\"dev\"}" 2>/dev/null || echo "000")
if [ "${HTTP}" = "201" ]; then
echo "Created branch: ${BRANCH}"
# Comment on issue with branch link
REPO_URL="${GITEA_URL}/${{ github.repository }}"
BODY="Branch created: [\`${BRANCH}\`](${REPO_URL}/src/branch/${BRANCH})\n\n\`\`\`bash\ngit fetch origin\ngit checkout ${BRANCH}\n\`\`\`"
curl -sf -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API}/issues/${ISSUE_NUM}/comments" \
-d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1
echo "Commented on issue #${ISSUE_NUM}"
else
echo "Failed to create branch (HTTP ${HTTP}) -- may already exist"
fi
-70
View File
@@ -1,70 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Notifications
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
# PATH: /.gitea/workflows/notify.yml
# VERSION: 01.00.00
# BRIEF: Push notifications via ntfy on release success or workflow failure
name: "Universal: Notifications"
on:
workflow_run:
workflows:
- "Joomla Build & Release"
- "Joomla Extension CI"
- "Deploy"
types:
- completed
permissions:
contents: read
env:
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-releases' }}
jobs:
notify:
name: Send Notification
runs-on: ubuntu-latest
if: >-
github.event.workflow_run.conclusion == 'success' ||
github.event.workflow_run.conclusion == 'failure'
steps:
- name: Notify on success (releases only)
if: >-
github.event.workflow_run.conclusion == 'success' &&
contains(github.event.workflow_run.name, 'Release')
run: |
REPO="${{ github.event.repository.name }}"
WORKFLOW="${{ github.event.workflow_run.name }}"
URL="${{ github.event.workflow_run.html_url }}"
curl -sS \
-H "Title: ${REPO} released" \
-H "Tags: white_check_mark,package" \
-H "Priority: default" \
-H "Click: ${URL}" \
-d "${WORKFLOW} completed successfully." \
"${NTFY_URL}/${NTFY_TOPIC}"
- name: Notify on failure
if: github.event.workflow_run.conclusion == 'failure'
run: |
REPO="${{ github.event.repository.name }}"
WORKFLOW="${{ github.event.workflow_run.name }}"
URL="${{ github.event.workflow_run.html_url }}"
curl -sS \
-H "Title: ${REPO} workflow failed" \
-H "Tags: x,warning" \
-H "Priority: high" \
-H "Click: ${URL}" \
-d "${WORKFLOW} failed. Check the run for details." \
"${NTFY_URL}/${NTFY_TOPIC}"
-51
View File
@@ -1,51 +0,0 @@
name: Publish MCP to npm
on:
push:
branches: [main]
paths:
- '.mokogitea/mcp/**'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Install and build
working-directory: .mokogitea/mcp
run: |
npm ci
npx tsc
- name: Check version change
id: version
working-directory: .mokogitea/mcp
run: |
LOCAL_VERSION=$(node -p "require('./package.json').version")
NPM_VERSION=$(npm view @mokoconsulting/mokogitea-mcp version 2>/dev/null || echo "0.0.0")
if [ "$LOCAL_VERSION" != "$NPM_VERSION" ]; then
echo "changed=true" >> $GITHUB_OUTPUT
echo "Version changed: $NPM_VERSION -> $LOCAL_VERSION"
else
echo "changed=false" >> $GITHUB_OUTPUT
echo "Version unchanged: $LOCAL_VERSION"
fi
- name: Publish to npm
if: steps.version.outputs.changed == 'true'
working-directory: .mokogitea/mcp
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish to Gitea registry
if: steps.version.outputs.changed == 'true'
working-directory: .mokogitea/mcp
run: |
npm publish --registry ${{ github.server_url }}/api/packages/${{ github.repository_owner }}/npm/ \
--//$(echo "${{ github.server_url }}" | sed 's|https://||')/api/packages/${{ github.repository_owner }}/npm/:_authToken=${{ secrets.GITEA_TOKEN }}
-90
View File
@@ -1,90 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Enforces branch merge policy:
# feature/* → dev only
# fix/* → dev only
# hotfix/* → dev or main (emergency)
# dev → main only
# alpha/* → dev only
# beta/* → dev only
# rc/* → main only
name: Branch Policy Check
on:
pull_request:
types: [opened, synchronize, reopened, edited]
jobs:
check-target:
name: Verify merge target
runs-on: ubuntu-latest
steps:
- name: Check branch policy
run: |
HEAD="${{ github.head_ref }}"
BASE="${{ github.base_ref }}"
echo "PR: ${HEAD} → ${BASE}"
ALLOWED=true
REASON=""
case "$HEAD" in
feature/*|feat/*)
if [ "$BASE" != "dev" ]; then
ALLOWED=false
REASON="Feature branches must target 'dev', not '${BASE}'"
fi
;;
fix/*|bugfix/*)
if [ "$BASE" != "dev" ]; then
ALLOWED=false
REASON="Fix branches must target 'dev', not '${BASE}'"
fi
;;
hotfix/*)
if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then
ALLOWED=false
REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'"
fi
;;
alpha/*|beta/*)
if [ "$BASE" != "dev" ]; then
ALLOWED=false
REASON="Pre-release branches must target 'dev', not '${BASE}'"
fi
;;
rc/*)
if [ "$BASE" != "main" ]; then
ALLOWED=false
REASON="Release candidate branches must target 'main', not '${BASE}'"
fi
;;
dev)
if [ "$BASE" != "main" ]; then
ALLOWED=false
REASON="Dev branch can only merge into 'main', not '${BASE}'"
fi
;;
esac
if [ "$ALLOWED" = false ]; then
echo "::error::${REASON}"
echo ""
echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "${REASON}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "Branch policy: OK (${HEAD} → ${BASE})"
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
-508
View File
@@ -1,508 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.CI
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
# PATH: /templates/workflows/universal/pr-check.yml.template
# VERSION: 09.23.00
# BRIEF: PR gate — branch policy + code validation before merge
name: "Universal: PR Check"
on:
pull_request:
types: [opened, synchronize, reopened, edited]
permissions:
contents: read
pull-requests: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
# ── Branch Policy ──────────────────────────────────────────────────────
branch-policy:
name: Branch Policy
runs-on: ubuntu-latest
steps:
- name: Check branch merge target
run: |
HEAD="${{ github.head_ref }}"
BASE="${{ github.base_ref }}"
echo "PR: ${HEAD} → ${BASE}"
ALLOWED=true
REASON=""
case "$HEAD" in
feature/*|feat/*)
if [ "$BASE" != "dev" ]; then
ALLOWED=false
REASON="Feature branches must target 'dev', not '${BASE}'"
fi
;;
fix/*|bugfix/*)
if [ "$BASE" != "dev" ]; then
ALLOWED=false
REASON="Fix branches must target 'dev', not '${BASE}'"
fi
;;
patch/*)
if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then
ALLOWED=false
REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'"
fi
;;
hotfix/*)
if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then
ALLOWED=false
REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'"
fi
;;
rc)
if [ "$BASE" != "main" ]; then
ALLOWED=false
REASON="RC branch can only merge into 'main', not '${BASE}'"
fi
;;
dev)
if [ "$BASE" != "main" ]; then
ALLOWED=false
REASON="Dev branch can only merge into 'main', not '${BASE}'"
fi
;;
esac
if [ "$ALLOWED" = false ]; then
echo "::error::${REASON}"
echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "${REASON}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "Branch policy: OK (${HEAD} → ${BASE})"
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
# ── Code Validation ────────────────────────────────────────────────────
validate:
name: Validate PR
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check for merge conflict markers
run: |
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
if [ -n "$CONFLICTS" ]; then
echo "::error::Merge conflict markers found in source files"
echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "No conflict markers found"
- name: Detect platform
id: platform
run: |
# Read platform from XML manifest (<platform> tag) or plain text fallback
PLATFORM=$(sed -n 's/.*<platform>\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1)
[ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]')
[ -z "$PLATFORM" ] && PLATFORM="generic"
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
- name: Setup PHP
if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr'
run: |
if ! command -v php &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
fi
- name: PHP syntax check
if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr'
run: |
ERRORS=0
while IFS= read -r -d '' file; do
if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then
ERRORS=$((ERRORS + 1))
fi
done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -print0)
echo "PHP lint: ${ERRORS} error(s)"
[ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; }
- name: Joomla JEXEC guard check
if: steps.platform.outputs.platform == 'joomla'
run: |
ERRORS=0
while IFS= read -r -d '' file; do
# Skip vendor, node_modules, and index.html stub files
case "$file" in ./vendor/*|./node_modules/*) continue ;; esac
# Check first 10 lines for JEXEC or JPATH guard
if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then
echo "::error file=${file}::Missing JEXEC guard: ${file}"
ERRORS=$((ERRORS + 1))
fi
done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0)
if [ "$ERRORS" -gt 0 ]; then
echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard"
echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY
echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "JEXEC guard: OK"
- name: Joomla directory listing protection
if: steps.platform.outputs.platform == 'joomla'
run: |
MISSING=0
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && exit 0
while IFS= read -r dir; do
if [ ! -f "${dir}/index.html" ]; then
echo "::warning::Missing index.html in ${dir} (directory listing protection)"
MISSING=$((MISSING + 1))
fi
done < <(find "$SOURCE_DIR" -type d -not -path "./.git/*" -not -path "*/vendor/*" -not -path "*/node_modules/*")
if [ "$MISSING" -gt 0 ]; then
echo "## Directory Protection" >> $GITHUB_STEP_SUMMARY
echo "${MISSING} director(ies) missing index.html" >> $GITHUB_STEP_SUMMARY
fi
echo "Directory protection: ${MISSING} missing (advisory)"
- name: Joomla script file and asset checks
if: steps.platform.outputs.platform == 'joomla'
run: |
ERRORS=0
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
[ -z "$MANIFEST" ] && exit 0
MANIFEST_DIR=$(dirname "$MANIFEST")
# Check scriptfile exists if declared
SCRIPTFILE=$(sed -n 's/.*<scriptfile>\([^<]*\)<\/scriptfile>.*/\1/p' "$MANIFEST" 2>/dev/null)
if [ -n "$SCRIPTFILE" ]; then
if [ ! -f "${MANIFEST_DIR}/${SCRIPTFILE}" ]; then
echo "::error::Manifest declares <scriptfile>${SCRIPTFILE}</scriptfile> but file not found at ${MANIFEST_DIR}/${SCRIPTFILE}"
ERRORS=$((ERRORS + 1))
else
echo "Script file: ${MANIFEST_DIR}/${SCRIPTFILE} (OK)"
fi
fi
# Require joomla.asset.json and validate it
ASSET_JSON=$(find "$MANIFEST_DIR" -name "joomla.asset.json" -not -path "./.git/*" 2>/dev/null | head -1)
if [ -z "$ASSET_JSON" ]; then
echo "::error::joomla.asset.json not found — Joomla asset system is required"
ERRORS=$((ERRORS + 1))
else
if command -v php &> /dev/null; then
php -r "json_decode(file_get_contents('$ASSET_JSON')); if(json_last_error()!==JSON_ERROR_NONE){echo json_last_error_msg();exit(1);}" 2>&1 || {
echo "::error::joomla.asset.json is not valid JSON"
ERRORS=$((ERRORS + 1))
}
fi
echo "joomla.asset.json: valid"
fi
# Validate all XML files in src/ are well-formed
XML_ERRORS=0
if command -v php &> /dev/null; then
while IFS= read -r -d '' xmlfile; do
if ! php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$xmlfile'); if(!\$x){foreach(libxml_get_errors() as \$e) echo trim(\$e->message) . ' in $xmlfile'; exit(1);}" 2>&1; then
XML_ERRORS=$((XML_ERRORS + 1))
fi
done < <(find "$MANIFEST_DIR" -name "*.xml" -not -path "./.git/*" -print0)
fi
if [ "$XML_ERRORS" -gt 0 ]; then
echo "::error::${XML_ERRORS} XML file(s) are malformed"
ERRORS=$((ERRORS + 1))
else
echo "XML well-formedness: OK"
fi
[ "$ERRORS" -gt 0 ] && exit 1
echo "Joomla asset checks: OK"
- name: Validate platform manifest
run: |
PLATFORM="${{ steps.platform.outputs.platform }}"
case "$PLATFORM" in
joomla)
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
if [ -z "$MANIFEST" ]; then
echo "::warning::No Joomla manifest found (WaaS site)"
exit 0
fi
echo "Manifest: ${MANIFEST}"
if command -v php &> /dev/null; then
php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$MANIFEST'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::Manifest XML is malformed"; exit 1; }
fi
for ELEMENT in name version description; do
grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; }
done
# Block legacy raw/branch update server URLs on MokoGitea
RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogitea\|git\.mokoconsulting\.tech' || true)
if [ -n "$RAW_URLS" ]; then
echo "::error::Manifest contains legacy raw/branch update server URL on MokoGitea. Use the Gitea Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)"
echo "$RAW_URLS"
exit 1
fi
echo "Joomla manifest valid"
;;
dolibarr)
MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1)
if [ -z "$MOD_FILE" ]; then
echo "::error::No mod*.class.php found"
exit 1
fi
echo "Dolibarr module: ${MOD_FILE}"
;;
*)
echo "Generic platform — no manifest validation"
;;
esac
- name: Check update stream format
run: |
PLATFORM="${{ steps.platform.outputs.platform }}"
case "$PLATFORM" in
joomla)
if [ -f "updates.xml" ]; then
if command -v php &> /dev/null; then
php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('updates.xml'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::updates.xml is malformed"; exit 1; }
fi
echo "updates.xml valid"
fi
;;
dolibarr)
[ -f "update.txt" ] && echo "update.txt present" || echo "::warning::No update.txt"
;;
esac
- name: Validate Joomla language files
if: steps.platform.outputs.platform == 'joomla'
run: |
ERRORS=0
WARNINGS=0
# Require both en-GB and en-US language directories
LANG_ROOT=$(find . -path "*/language" -type d -not -path "./.git/*" 2>/dev/null | head -1)
if [ -z "$LANG_ROOT" ]; then
echo "No language/ directory found — skipping"
exit 0
fi
if [ ! -d "$LANG_ROOT/en-GB" ]; then
echo "::error::Missing en-GB language directory (${LANG_ROOT}/en-GB)"
ERRORS=$((ERRORS + 1))
fi
if [ ! -d "$LANG_ROOT/en-US" ]; then
echo "::error::Missing en-US language directory (${LANG_ROOT}/en-US)"
ERRORS=$((ERRORS + 1))
fi
# Check that en-GB and en-US have matching .ini files
if [ -d "$LANG_ROOT/en-GB" ] && [ -d "$LANG_ROOT/en-US" ]; then
for GB_INI in "$LANG_ROOT/en-GB"/*.ini; do
[ ! -f "$GB_INI" ] && continue
US_INI="$LANG_ROOT/en-US/$(basename "$GB_INI")"
if [ ! -f "$US_INI" ]; then
echo "::error::$(basename "$GB_INI") exists in en-GB but missing from en-US"
ERRORS=$((ERRORS + 1))
fi
done
for US_INI in "$LANG_ROOT/en-US"/*.ini; do
[ ! -f "$US_INI" ] && continue
GB_INI="$LANG_ROOT/en-GB/$(basename "$US_INI")"
if [ ! -f "$GB_INI" ]; then
echo "::error::$(basename "$US_INI") exists in en-US but missing from en-GB"
ERRORS=$((ERRORS + 1))
fi
done
fi
# Find all .ini language files
INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null)
if [ -z "$INI_FILES" ]; then
echo "No .ini language files found"
[ "$ERRORS" -gt 0 ] && exit 1
exit 0
fi
echo "Found $(echo "$INI_FILES" | wc -l) language file(s)"
for FILE in $INI_FILES; do
FNAME=$(basename "$FILE")
LINENUM=0
SEEN_KEYS=""
while IFS= read -r line || [ -n "$line" ]; do
LINENUM=$((LINENUM + 1))
# Skip empty lines and comments
[ -z "$line" ] && continue
echo "$line" | grep -qE '^\s*;' && continue
echo "$line" | grep -qE '^\s*$' && continue
# Must match KEY="VALUE" format
if ! echo "$line" | grep -qE '^[A-Z_][A-Z0-9_]*=".*"$'; then
echo "::error file=${FILE},line=${LINENUM}::Malformed line: ${line}"
ERRORS=$((ERRORS + 1))
continue
fi
# Extract key and check for duplicates
KEY=$(echo "$line" | sed 's/=.*//')
if echo "$SEEN_KEYS" | grep -qx "$KEY"; then
echo "::error file=${FILE},line=${LINENUM}::Duplicate key: ${KEY}"
ERRORS=$((ERRORS + 1))
fi
SEEN_KEYS="${SEEN_KEYS}
${KEY}"
done < "$FILE"
echo " ${FILE}: checked ${LINENUM} lines"
done
# Cross-check en-GB vs en-US key consistency
GB_DIR=$(find . -path "*/language/en-GB" -type d -not -path "./.git/*" 2>/dev/null | head -1)
US_DIR=$(find . -path "*/language/en-US" -type d -not -path "./.git/*" 2>/dev/null | head -1)
if [ -n "$GB_DIR" ] && [ -n "$US_DIR" ]; then
for GB_FILE in "$GB_DIR"/*.ini; do
[ ! -f "$GB_FILE" ] && continue
FNAME=$(basename "$GB_FILE")
US_FILE="$US_DIR/$FNAME"
[ ! -f "$US_FILE" ] && continue
GB_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$GB_FILE" 2>/dev/null | sort)
US_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$US_FILE" 2>/dev/null | sort)
# Keys in en-GB but not en-US
MISSING_US=$(comm -23 <(echo "$GB_KEYS") <(echo "$US_KEYS"))
if [ -n "$MISSING_US" ]; then
echo "::warning::Keys in en-GB/$FNAME but missing from en-US/$FNAME:"
echo "$MISSING_US" | while read -r k; do echo " - $k"; done
WARNINGS=$((WARNINGS + 1))
fi
# Keys in en-US but not en-GB
MISSING_GB=$(comm -13 <(echo "$GB_KEYS") <(echo "$US_KEYS"))
if [ -n "$MISSING_GB" ]; then
echo "::warning::Keys in en-US/$FNAME but missing from en-GB/$FNAME:"
echo "$MISSING_GB" | while read -r k; do echo " - $k"; done
WARNINGS=$((WARNINGS + 1))
fi
done
fi
{
echo "### Language File Validation"
echo "| Metric | Count |"
echo "|---|---|"
echo "| Files checked | $(echo "$INI_FILES" | wc -l) |"
echo "| Errors | ${ERRORS} |"
echo "| Warnings | ${WARNINGS} |"
} >> $GITHUB_STEP_SUMMARY
if [ "$ERRORS" -gt 0 ]; then
echo "::error::Language validation failed with ${ERRORS} error(s)"
exit 1
fi
echo "Language files: OK (${WARNINGS} warning(s))"
- name: Check changelog has unreleased entry
run: |
if [ ! -f "CHANGELOG.md" ]; then
echo "::warning::No CHANGELOG.md found"
exit 0
fi
# Check for content under [Unreleased] section
if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then
echo "::error::CHANGELOG.md missing [Unreleased] section"
exit 1
fi
# Check there's at least one entry (Added/Changed/Fixed/Removed) under Unreleased
UNRELEASED_CONTENT=$(sed -n '/## \[Unreleased\]/,/## \[/p' CHANGELOG.md | grep -cE '^\s*-\s' || true)
if [ "$UNRELEASED_CONTENT" -eq 0 ]; then
echo "::error::CHANGELOG.md [Unreleased] section has no entries. Add a changelog entry describing your changes."
echo "## Changelog Check: Failed" >> $GITHUB_STEP_SUMMARY
echo "The \`[Unreleased]\` section in CHANGELOG.md has no entries." >> $GITHUB_STEP_SUMMARY
echo "Add a line like \`- Description of your change\` under a heading (\`### Added\`, \`### Changed\`, \`### Fixed\`, etc.)" >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "Changelog: ${UNRELEASED_CONTENT} entry/entries in [Unreleased]"
- name: Verify package source
run: |
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
if [ ! -d "$SOURCE_DIR" ]; then
echo "::warning::No src/ or htdocs/ directory"
exit 0
fi
FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l)
echo "Source: ${FILE_COUNT} files"
[ "$FILE_COUNT" -gt 0 ] || { echo "::error::Source directory is empty"; exit 1; }
# ── Pre-Release RC Build ─────────────────────────────────────────────────
pre-release:
name: Build RC Package
runs-on: ubuntu-latest
needs: [branch-policy, validate]
steps:
- name: Trigger RC pre-release
env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
REPO: ${{ github.repository }}
BRANCH: ${{ github.head_ref }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: |
curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
# ── Issue Reporter ──────────────────────────────────────────────────────
report-issues:
name: Report Issues
runs-on: ubuntu-latest
needs: [branch-policy, validate]
if: >-
always() &&
needs.validate.result == 'failure'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: automation/ci-issue-reporter.sh
sparse-checkout-cone-mode: false
- name: "File issue for PR validation failure"
env:
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: |
chmod +x automation/ci-issue-reporter.sh
./automation/ci-issue-reporter.sh \
--gate "PR Validation" \
--workflow "PR Check" \
--severity error \
--details "PR validation failed (syntax, manifest, changelog, or source checks). See the CI run for the specific check that failed."
-170
View File
@@ -1,170 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# BRIEF: Auto-build RC release on PR to main, update RC update stream
name: "PR RC Release"
on:
pull_request:
types: [opened, synchronize]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
REGISTRY: code.mokoconsulting.tech
IMAGE: mokoconsulting/mokogitea
permissions:
contents: write
jobs:
rc-release:
name: Build RC Release
runs-on: ubuntu-latest
steps:
- name: Check target branch
id: guard
env:
BASE_BRANCH: ${{ github.base_ref }}
run: |
echo "PR target: ${BASE_BRANCH}"
if [ "$BASE_BRANCH" != "main" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Skipping RC — only for PRs targeting main"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Checkout PR branch
if: steps.guard.outputs.skip != 'true'
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Determine RC version
if: steps.guard.outputs.skip != 'true'
id: version
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
BASE_VERSION=$(sed -n 's/.*<version>\(.*\)<\/version>.*/\1/p' updates.xml | head -1)
[ -z "$BASE_VERSION" ] && BASE_VERSION="04.00.00"
RC_VERSION="${BASE_VERSION}-rc.${PR_NUMBER}"
RC_TAG="v1.26.1-moko.${RC_VERSION}"
echo "version=$RC_VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$RC_TAG" >> "$GITHUB_OUTPUT"
echo "RC version: $RC_VERSION (tag: $RC_TAG)"
- name: Update updates.xml RC channel
if: steps.guard.outputs.skip != 'true'
env:
RC_VERSION: ${{ steps.version.outputs.version }}
RC_TAG: ${{ steps.version.outputs.tag }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_NUM: ${{ github.event.pull_request.number }}
run: |
DOCKER_TAG="${REGISTRY}/${IMAGE}:${RC_TAG}"
python3 << 'PYEOF'
import os, re
rc_version = os.environ["RC_VERSION"]
rc_tag = os.environ["RC_TAG"]
pr_url = os.environ["PR_URL"]
pr_num = os.environ["PR_NUM"]
docker_tag = os.environ["REGISTRY"] + "/" + os.environ["IMAGE"] + ":" + rc_tag
entry = f""" <update>
<name>MokoGitea</name>
<description>MokoGitea RC from PR #{pr_num}</description>
<element>mokogitea</element>
<type>application</type>
<version>{rc_version}</version>
<client>server</client>
<tags><tag>rc</tag></tags>
<infourl title="MokoGitea RC">{pr_url}</infourl>
<downloads>
<downloadurl type="full" format="docker">{docker_tag}</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\\.25\\.)|(1\\.26\\.))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>"""
content = open("updates.xml").read()
# Remove existing RC entry
content = re.sub(
r"\s*<update>[\s\S]*?<tag>rc</tag>[\s\S]*?</update>",
"",
content,
)
# Insert before </updates>
content = content.replace("</updates>", entry + "\n</updates>")
open("updates.xml", "w").write(content)
print(f"Updated updates.xml with RC entry: {rc_version}")
PYEOF
- name: Create RC release
if: steps.guard.outputs.skip != 'true'
env:
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
RC_TAG: ${{ steps.version.outputs.tag }}
RC_VERSION: ${{ steps.version.outputs.version }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
API_BASE: https://${{ env.REGISTRY }}/api/v1/repos/${{ github.repository }}
run: |
# Delete existing RC release/tag if present
curl -s -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/releases/tags/${RC_TAG}" 2>/dev/null || true
curl -s -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/tags/${RC_TAG}" 2>/dev/null || true
# Create prerelease
python3 << PYEOF
import json, os, urllib.request
api = os.environ["API_BASE"]
token = os.environ["GITEA_TOKEN"]
payload = json.dumps({
"tag_name": os.environ["RC_TAG"],
"target_commitish": os.environ["HEAD_SHA"],
"name": f"RC: {os.environ['PR_TITLE']}",
"body": f"Release candidate from PR #{os.environ['PR_NUMBER']}\n\nPR: {os.environ['PR_URL']}\nDocker: docker pull {os.environ['REGISTRY']}/{os.environ['IMAGE']}:{os.environ['RC_TAG']}",
"draft": False,
"prerelease": True,
}).encode()
req = urllib.request.Request(
f"{api}/releases",
data=payload,
headers={
"Authorization": f"token {token}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req) as resp:
result = json.loads(resp.read())
print(f"Created RC release: {result.get('tag_name')}")
PYEOF
- name: Commit updates.xml
if: steps.guard.outputs.skip != 'true'
env:
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
PR_NUM: ${{ github.event.pull_request.number }}
run: |
git config user.name "MokoGitea Bot"
git config user.email "deploy@mokoconsulting.tech"
git add updates.xml
if git diff --cached --quiet; then
echo "No changes to updates.xml"
else
git commit -m "chore(ci): update RC stream for PR #${PR_NUM}"
git push origin "HEAD:${HEAD_REF}" || echo "Push failed"
fi
-11
View File
@@ -1,11 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /templates/workflows/universal/pre-release.yml.template
# VERSION: 05.01.00
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
-66
View File
@@ -1,66 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoPlatform.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
# PATH: /.mokogitea/workflows/rc-revert.yml
# VERSION: 09.23.00
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
name: "RC Revert"
on:
pull_request:
types: [closed]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
revert:
name: Rename rc/ back to dev/
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == false &&
startsWith(github.event.pull_request.head.ref, 'rc/')
steps:
- name: Rename branch
run: |
BRANCH="${{ github.event.pull_request.head.ref }}"
SUFFIX="${BRANCH#rc/}"
DEV_BRANCH="dev/${SUFFIX}"
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Create dev/ branch from rc/ branch
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"new_branch_name\": \"${DEV_BRANCH}\", \"old_branch_name\": \"${BRANCH}\"}" \
"${API}" 2>/dev/null || true)
if [ "$STATUS" = "201" ]; then
echo "Created branch: ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
else
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"
exit 1
fi
# Delete rc/ branch
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
-H "Authorization: token ${TOKEN}" \
"${API}/${ENCODED}" 2>/dev/null || true)
if [ "$STATUS" = "204" ]; then
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
else
echo "::warning::Failed to delete ${BRANCH} (HTTP ${STATUS})"
fi
echo "### RC Reverted" >> $GITHUB_STEP_SUMMARY
echo "${BRANCH} → ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
-711
View File
@@ -1,711 +0,0 @@
# ============================================================================
# Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
#
# This file is part of a Moko Consulting project.
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Validation
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
# PATH: /templates/workflows/joomla/repo_health.yml.template
# VERSION: 09.23.00
# BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts.
# ============================================================================
name: "Generic: Repo Health"
defaults:
run:
shell: bash
on:
workflow_dispatch:
inputs:
profile:
description: 'Validation profile: all, scripts, or repo'
required: true
default: all
type: choice
options:
- all
- scripts
- repo
pull_request:
push:
permissions:
contents: read
env:
# Scripts governance policy
SCRIPTS_REQUIRED_DIRS:
SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate
# Repo health policy
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogitea/workflows/
REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/
REPO_DISALLOWED_DIRS:
REPO_DISALLOWED_FILES: TODO.md,todo.md
# Extended checks toggles
EXTENDED_CHECKS: "true"
# File / directory variables
DOCS_INDEX: docs/docs-index.md
SCRIPT_DIR: scripts
WORKFLOWS_DIR: .mokogitea/workflows
SHELLCHECK_PATTERN: '*.sh'
SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml'
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
access_check:
name: Access control
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
outputs:
allowed: ${{ steps.perm.outputs.allowed }}
permission: ${{ steps.perm.outputs.permission }}
steps:
- name: Check actor permission (admin only)
id: perm
env:
TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }}
REPO: ${{ github.repository }}
ACTOR: ${{ github.actor }}
run: |
set -euo pipefail
ALLOWED=false
PERMISSION=unknown
METHOD=""
# Hardcoded authorized users — always allowed
case "$ACTOR" in
jmiller|gitea-actions[bot])
ALLOWED=true
PERMISSION=admin
METHOD="hardcoded allowlist"
;;
*)
# Detect platform and check permissions via API
API_BASE="${GITHUB_API_URL:-${GITEA_API_URL:-https://api.github.com}}"
RESP=$(curl -sf -H "Authorization: token ${TOKEN}" \
"${API_BASE}/repos/${REPO}/collaborators/${ACTOR}/permission" 2>/dev/null || echo '{}')
PERMISSION=$(echo "$RESP" | grep -oP '"permission"\s*:\s*"\K[^"]+' || echo "unknown")
if [ "$PERMISSION" = "admin" ] || [ "$PERMISSION" = "maintain" ] || [ "$PERMISSION" = "owner" ]; then
ALLOWED=true
fi
METHOD="collaborator API"
;;
esac
echo "permission=${PERMISSION}" >> "$GITHUB_OUTPUT"
echo "allowed=${ALLOWED}" >> "$GITHUB_OUTPUT"
{
echo "## Access Authorization"
echo ""
echo "| Field | Value |"
echo "|-------|-------|"
echo "| **Actor** | \`${ACTOR}\` |"
echo "| **Repository** | \`${REPO}\` |"
echo "| **Permission** | \`${PERMISSION}\` |"
echo "| **Method** | ${METHOD} |"
echo "| **Authorized** | ${ALLOWED} |"
echo ""
if [ "$ALLOWED" = "true" ]; then
echo "${ACTOR} authorized (${METHOD})"
else
echo "${ACTOR} is NOT authorized. Requires admin or maintain role."
fi
} >> "${GITHUB_STEP_SUMMARY}"
- name: Deny execution when not permitted
if: ${{ steps.perm.outputs.allowed != 'true' }}
run: |
set -euo pipefail
printf '%s\n' 'ERROR: Access denied. Admin permission required.' >> "${GITHUB_STEP_SUMMARY}"
exit 1
scripts_governance:
name: Scripts governance
needs: access_check
if: ${{ needs.access_check.outputs.allowed == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Scripts folder checks
env:
PROFILE_RAW: ${{ github.event.inputs.profile }}
run: |
set -euo pipefail
profile="${PROFILE_RAW:-all}"
case "${profile}" in
all|scripts|repo) ;;
*)
printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}"
exit 1
;;
esac
if [ "${profile}" = 'repo' ]; then
{
printf '%s\n' '### Scripts governance'
printf '%s\n' "Profile: ${profile}"
printf '%s\n' 'Status: SKIPPED'
printf '%s\n' 'Reason: profile excludes scripts governance'
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
exit 0
fi
if [ ! -d "${SCRIPT_DIR}" ]; then
{
printf '%s\n' '### Scripts governance'
printf '%s\n' 'Status: OK (advisory)'
printf '%s\n' 'scripts/ directory not present. No scripts governance enforced.'
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
exit 0
fi
if [ -n "${SCRIPTS_REQUIRED_DIRS:-}" ]; then IFS=',' read -r -a required_dirs <<< "${SCRIPTS_REQUIRED_DIRS}"; else required_dirs=(); fi
IFS=',' read -r -a allowed_dirs <<< "${SCRIPTS_ALLOWED_DIRS}"
missing_dirs=()
unapproved_dirs=()
for d in "${required_dirs[@]}"; do
req="${d%/}"
[ ! -d "${req}" ] && missing_dirs+=("${req}/")
done
while IFS= read -r d; do
allowed=false
for a in "${allowed_dirs[@]}"; do
a_norm="${a%/}"
[ "${d%/}" = "${a_norm}" ] && allowed=true
done
[ "${allowed}" = false ] && unapproved_dirs+=("${d%/}/")
done < <(find "${SCRIPT_DIR}" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sed 's#^\./##')
{
printf '%s\n' '### Scripts governance'
printf '%s\n' "Profile: ${profile}"
printf '%s\n' '| Area | Status | Notes |'
printf '%s\n' '|---|---|---|'
if [ "${#missing_dirs[@]}" -gt 0 ]; then
printf '%s\n' '| Required directories | Warning | Missing required subfolders |'
else
printf '%s\n' '| Required directories | OK | All required subfolders present |'
fi
if [ "${#unapproved_dirs[@]}" -gt 0 ]; then
printf '%s\n' '| Directory policy | Warning | Unapproved directories detected |'
else
printf '%s\n' '| Directory policy | OK | No unapproved directories |'
fi
printf '%s\n' '| Enforcement mode | Advisory | scripts folder is optional |'
printf '\n'
if [ "${#missing_dirs[@]}" -gt 0 ]; then
printf '%s\n' 'Missing required script directories:'
for m in "${missing_dirs[@]}"; do printf '%s\n' "- ${m}"; done
printf '\n'
else
printf '%s\n' 'Missing required script directories: none.'
printf '\n'
fi
if [ "${#unapproved_dirs[@]}" -gt 0 ]; then
printf '%s\n' 'Unapproved script directories detected:'
for m in "${unapproved_dirs[@]}"; do printf '%s\n' "- ${m}"; done
printf '\n'
else
printf '%s\n' 'Unapproved script directories detected: none.'
printf '\n'
fi
printf '%s\n' 'Scripts governance completed in advisory mode.'
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
repo_health:
name: Repository health
needs: access_check
if: ${{ needs.access_check.outputs.allowed == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Repository health checks
env:
PROFILE_RAW: ${{ github.event.inputs.profile }}
run: |
set -euo pipefail
profile="${PROFILE_RAW:-all}"
case "${profile}" in
all|scripts|repo) ;;
*)
printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}"
exit 1
;;
esac
if [ "${profile}" = 'scripts' ]; then
{
printf '%s\n' '### Repository health'
printf '%s\n' "Profile: ${profile}"
printf '%s\n' 'Status: SKIPPED'
printf '%s\n' 'Reason: profile excludes repository health'
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
exit 0
fi
IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}"
IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}"
if [ -n "${REPO_DISALLOWED_DIRS:-}" ]; then IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"; else disallowed_dirs=(); fi
IFS=',' read -r -a disallowed_files <<< "${REPO_DISALLOWED_FILES:-}"
missing_required=()
missing_optional=()
# Source directory: src/ or htdocs/ (either is valid for extension repos)
SOURCE_DIR=""
if [ -d "src" ]; then
SOURCE_DIR="src"
elif [ -d "htdocs" ]; then
SOURCE_DIR="htdocs"
elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then
# Platform/tooling repos don't need src/
SOURCE_DIR=""
else
missing_required+=("src/ or htdocs/ (source directory required)")
fi
for item in "${required_artifacts[@]}"; do
if printf '%s' "${item}" | grep -q '/$'; then
d="${item%/}"
[ ! -d "${d}" ] && missing_required+=("${item}")
else
[ ! -f "${item}" ] && missing_required+=("${item}")
fi
done
for f in "${optional_files[@]}"; do
if printf '%s' "${f}" | grep -q '/$'; then
d="${f%/}"
[ ! -d "${d}" ] && missing_optional+=("${f}")
else
[ ! -f "${f}" ] && missing_optional+=("${f}")
fi
done
for d in "${disallowed_dirs[@]}"; do
d_norm="${d%/}"
[ -d "${d_norm}" ] && missing_required+=("${d_norm}/ (disallowed)")
done
for f in "${disallowed_files[@]}"; do
[ -f "${f}" ] && missing_required+=("${f} (disallowed)")
done
git fetch origin --prune
dev_paths=()
dev_branches=()
while IFS= read -r b; do
name="${b#origin/}"
if [ "${name}" = 'dev' ]; then
dev_branches+=("${name}")
else
dev_paths+=("${name}")
fi
done < <(git branch -r --list 'origin/dev*' | sed 's/^ *//')
if [ "${#dev_paths[@]}" -eq 0 ] && [ "${#dev_branches[@]}" -eq 0 ]; then
missing_required+=("dev or dev/* branch")
fi
content_warnings=()
if [ -f 'CHANGELOG.md' ] && ! grep -Eq '^# Changelog' CHANGELOG.md; then
content_warnings+=("CHANGELOG.md missing '# Changelog' header")
fi
if [ -f 'CHANGELOG.md' ] && grep -Eq '^[# ]*Unreleased' CHANGELOG.md; then
content_warnings+=("CHANGELOG.md contains Unreleased section (review release readiness)")
fi
if [ -f 'LICENSE' ] && ! grep -qiE 'GNU GENERAL PUBLIC LICENSE|GPL' LICENSE; then
content_warnings+=("LICENSE does not look like a GPL text")
fi
if [ -f 'README.md' ] && ! grep -qiE 'moko|Moko' README.md; then
content_warnings+=("README.md missing expected brand keyword")
fi
export PROFILE_RAW="${profile}"
export MISSING_REQUIRED="$(printf '%s\n' "${missing_required[@]:-}")"
export MISSING_OPTIONAL="$(printf '%s\n' "${missing_optional[@]:-}")"
export CONTENT_WARNINGS="$(printf '%s\n' "${content_warnings[@]:-}")"
report_json=$(printf '{"profile":"%s","missing_required":%d,"missing_optional":%d,"content_warnings":%d}' "$profile" "${#missing_required[@]}" "${#missing_optional[@]}" "${#content_warnings[@]}")
{
printf '%s\n' '### Repository health'
printf '%s\n' "Profile: ${profile}"
printf '%s\n' '| Metric | Value |'
printf '%s\n' '|---|---|'
printf '%s\n' "| Missing required | ${#missing_required[@]} |"
printf '%s\n' "| Missing optional | ${#missing_optional[@]} |"
printf '%s\n' "| Content warnings | ${#content_warnings[@]} |"
printf '\n'
printf '%s\n' '### Guardrails report (JSON)'
printf '%s\n' '```json'
printf '%s\n' "${report_json}"
printf '%s\n' '```'
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
if [ "${#missing_required[@]}" -gt 0 ]; then
{
printf '%s\n' '### Missing required repo artifacts'
for m in "${missing_required[@]}"; do printf '%s\n' "- ${m}"; done
printf '%s\n' 'ERROR: Guardrails failed. Missing required repository artifacts.'
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
exit 1
fi
if [ "${#missing_optional[@]}" -gt 0 ]; then
{
printf '%s\n' '### Missing optional repo artifacts'
for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
fi
if [ "${#content_warnings[@]}" -gt 0 ]; then
{
printf '%s\n' '### Repo content warnings'
for m in "${content_warnings[@]}"; do printf '%s\n' "- ${m}"; done
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
fi
# -- Joomla-specific checks --
joomla_findings=()
MANIFEST="$(find . -maxdepth 2 -name '*.xml' -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)"
if [ -z "${MANIFEST}" ]; then
joomla_findings+=("Joomla XML manifest not found (no *.xml with <extension> tag)")
else
if ! grep -qP '<version>' "${MANIFEST}"; then
joomla_findings+=("XML manifest: <version> tag missing")
fi
if ! grep -qP 'type="(component|module|plugin|library|package|template|language)"' "${MANIFEST}"; then
joomla_findings+=("XML manifest: type attribute missing or invalid")
fi
if ! grep -qP '<name>' "${MANIFEST}"; then
joomla_findings+=("XML manifest: <name> tag missing")
fi
if ! grep -qP '<author>' "${MANIFEST}"; then
joomla_findings+=("XML manifest: <author> tag missing")
fi
if ! grep -qP '<namespace' "${MANIFEST}"; then
joomla_findings+=("XML manifest: <namespace> missing (required for Joomla 5+)")
fi
fi
INI_COUNT="$(find . -name '*.ini' -type f 2>/dev/null | wc -l)"
if [ "${INI_COUNT}" -eq 0 ]; then
joomla_findings+=("No .ini language files found")
fi
if [ ! -f 'updates.xml' ]; then
joomla_findings+=("updates.xml missing in root (required for Joomla update server)")
fi
if [ -n "${SOURCE_DIR}" ]; then
INDEX_DIRS=("${SOURCE_DIR}" "${SOURCE_DIR}/admin" "${SOURCE_DIR}/site")
for dir in "${INDEX_DIRS[@]}"; do
if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then
joomla_findings+=("${dir}/index.html missing (directory listing protection)")
fi
done
fi
if [ "${#joomla_findings[@]}" -gt 0 ]; then
{
printf '%s\n' '### Joomla extension checks'
printf '%s\n' '| Check | Status |'
printf '%s\n' '|---|---|'
for f in "${joomla_findings[@]}"; do
printf '%s\n' "| ${f} | Warning |"
done
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
else
{
printf '%s\n' '### Joomla extension checks'
printf '%s\n' 'All Joomla-specific checks passed.'
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
fi
extended_enabled="${EXTENDED_CHECKS:-true}"
extended_findings=()
if [ "${extended_enabled}" = 'true' ]; then
if [ -f '.github/CODEOWNERS' ] || [ -f 'CODEOWNERS' ] || [ -f 'docs/CODEOWNERS' ]; then
:
else
extended_findings+=("CODEOWNERS not found (.github/CODEOWNERS preferred)")
fi
if ls "${WORKFLOWS_DIR}"/*.yml >/dev/null 2>&1 || ls "${WORKFLOWS_DIR}"/*.yaml >/dev/null 2>&1; then
bad_refs="$(grep -RIn --include='*.yml' --include='*.yaml' -E '^[[:space:]]*uses:[[:space:]]*[^#]+@(main|master)\b' "${WORKFLOWS_DIR}" 2>/dev/null || true)"
if [ -n "${bad_refs}" ]; then
extended_findings+=("Workflows reference actions @main/@master (pin versions): see log excerpt")
{
printf '%s\n' '### Workflow pinning advisory'
printf '%s\n' 'Found uses: entries pinned to main/master:'
printf '%s\n' '```'
printf '%s\n' "${bad_refs}"
printf '%s\n' '```'
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
fi
fi
if [ -f "${DOCS_INDEX}" ]; then
missing_links=""
while IFS= read -r docline; do
for link in $(echo "$docline" | grep -oE '\]\([^)]+\)' | sed 's/\](//' | sed 's/)$//' || true); do
case "$link" in http://*|https://*|"#"*|mailto:*) continue ;; esac
linkpath="${link%%#*}"
linkpath="${linkpath%%\?*}"
[ -z "$linkpath" ] && continue
if [ "${linkpath:0:1}" = "/" ]; then
testpath="${linkpath#/}"
else
testpath="$(dirname "${DOCS_INDEX}")/${linkpath}"
fi
[ ! -e "$testpath" ] && missing_links="${missing_links}${testpath} "
done
done < "${DOCS_INDEX}"
if [ -n "${missing_links}" ]; then
extended_findings+=("docs/docs-index.md contains broken relative links")
{
printf '%s\n' '### Docs index link integrity'
printf '%s\n' 'Broken relative links:'
for bl in ${missing_links}; do
printf '%s\n' "- ${bl}"
done
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
fi
fi
if [ -d "${SCRIPT_DIR}" ]; then
if ! command -v shellcheck >/dev/null 2>&1; then
sudo apt-get update -qq
sudo apt-get install -y shellcheck >/dev/null
fi
sc_out=''
while IFS= read -r shf; do
[ -z "${shf}" ] && continue
out_one="$(shellcheck -S warning -x "${shf}" 2>/dev/null || true)"
if [ -n "${out_one}" ]; then
sc_out="${sc_out}${out_one}\n"
fi
done < <(find "${SCRIPT_DIR}" -type f -name "${SHELLCHECK_PATTERN}" 2>/dev/null | sort)
if [ -n "${sc_out}" ]; then
extended_findings+=("ShellCheck warnings detected (advisory)")
sc_head="$(printf '%s' "${sc_out}" | head -n 200)"
{
printf '%s\n' '### ShellCheck (advisory)'
printf '%s\n' '```'
printf '%s\n' "${sc_head}"
printf '%s\n' '```'
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
fi
fi
spdx_missing=()
IFS=',' read -r -a spdx_globs <<< "${SPDX_FILE_GLOBS}"
spdx_args=()
for g in "${spdx_globs[@]}"; do spdx_args+=("${g}"); done
while IFS= read -r f; do
[ -z "${f}" ] && continue
if ! head -n 40 "${f}" | grep -q 'SPDX-License-Identifier:'; then
spdx_missing+=("${f}")
fi
done < <(git ls-files "${spdx_args[@]}" 2>/dev/null || true)
if [ "${#spdx_missing[@]}" -gt 0 ]; then
extended_findings+=("SPDX header missing in some tracked files (advisory)")
{
printf '%s\n' '### SPDX header advisory'
printf '%s\n' 'Files missing SPDX-License-Identifier (first 40 lines scan):'
for f in "${spdx_missing[@]}"; do printf '%s\n' "- ${f}"; done
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
fi
stale_cutoff_days=180
stale_branches="$(git for-each-ref --format='%(refname:short) %(committerdate:unix)' refs/remotes/origin 2>/dev/null | awk -v now="$(date +%s)" -v days="${stale_cutoff_days}" '{if (now-$2 > days*86400) print $1}' | head -50)"
if [ -n "${stale_branches}" ]; then
extended_findings+=("Stale remote branches detected (advisory)")
{
printf '%s\n' '### Git hygiene advisory'
printf '%s\n' "Branches with last commit older than ${stale_cutoff_days} days (sample up to 50):"
while IFS= read -r b; do [ -n "${b}" ] && printf '%s\n' "- ${b}"; done <<< "${stale_branches}"
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
fi
fi
{
printf '%s\n' '### Guardrails coverage matrix'
printf '%s\n' '| Domain | Status | Notes |'
printf '%s\n' '|---|---|---|'
printf '%s\n' '| Access control | OK | Admin-only execution gate |'
printf '%s\n' '| Release policy | N/A | Releases handled by MokoGitea |'
printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |'
printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |'
printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |'
if [ "${extended_enabled}" = 'true' ]; then
if [ "${#extended_findings[@]}" -gt 0 ]; then
printf '%s\n' '| Extended checks | Warning | See extended findings below |'
else
printf '%s\n' '| Extended checks | OK | No findings |'
fi
else
printf '%s\n' '| Extended checks | SKIPPED | EXTENDED_CHECKS disabled |'
fi
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
if [ "${extended_enabled}" = 'true' ] && [ "${#extended_findings[@]}" -gt 0 ]; then
{
printf '%s\n' '### Extended findings (advisory)'
for f in "${extended_findings[@]}"; do printf '%s\n' "- ${f}"; done
printf '\n'
} >> "${GITHUB_STEP_SUMMARY}"
fi
printf '%s\n' 'Repository health guardrails passed.' >> "${GITHUB_STEP_SUMMARY}"
site-health:
name: Site Health
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch'
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Uptime check
if: env.URLS != ''
run: |
echo "$URLS" > /tmp/urls.txt
php monitoring/uptime-probe.php --urls /tmp/urls.txt --timeout 15 || echo "::warning::Some sites are down"
rm -f /tmp/urls.txt
env:
URLS: ${{ vars.MONITORED_URLS }}
- name: SSL certificate check
if: env.DOMAINS != ''
run: |
echo "$DOMAINS" > /tmp/domains.txt
php monitoring/ssl-check.php --domains /tmp/domains.txt --warn-days 30 || echo "::warning::SSL certificates expiring soon"
rm -f /tmp/domains.txt
env:
DOMAINS: ${{ vars.MONITORED_DOMAINS }}
- name: Summary
if: always()
run: |
echo "### Site Health" >> $GITHUB_STEP_SUMMARY
echo "Uptime and SSL checks completed." >> $GITHUB_STEP_SUMMARY
# ═══════════════════════════════════════════════════════════════════════
# Issue Reporter — file issues for failed gates
# ═══════════════════════════════════════════════════════════════════════
report-issues:
name: "Report Issues"
runs-on: ubuntu-latest
needs: [access_check, scripts_governance, repo_health]
if: >-
always() &&
(needs.scripts_governance.result == 'failure' ||
needs.repo_health.result == 'failure')
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: automation/ci-issue-reporter.sh
sparse-checkout-cone-mode: false
- name: "File issues for failed gates"
env:
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: |
chmod +x automation/ci-issue-reporter.sh
REPORTER="./automation/ci-issue-reporter.sh"
WF="Repo Health"
report_gate() {
local gate="$1" result="$2" details="$3"
if [ "$result" = "failure" ]; then
"$REPORTER" --gate "$gate" --details "$details" --workflow "$WF" --severity error
fi
}
report_gate "Scripts Governance" \
"${{ needs.scripts_governance.result }}" \
"Scripts directory policy violations detected. Review required and allowed directories."
report_gate "Repository Health" \
"${{ needs.repo_health.result }}" \
"Repository health checks failed — missing required artifacts, disallowed files, or content warnings. Check the CI run summary."
-82
View File
@@ -1,82 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Security
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
# PATH: /.gitea/workflows/security-audit.yml
# VERSION: 01.00.00
# BRIEF: Dependency vulnerability scanning for composer and npm packages
name: "Universal: Security Audit"
on:
schedule:
- cron: '0 6 * * 1' # Weekly on Monday at 06:00 UTC
pull_request:
branches:
- main
paths:
- 'composer.json'
- 'composer.lock'
- 'package.json'
- 'package-lock.json'
workflow_dispatch:
permissions:
contents: read
env:
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }}
jobs:
audit:
name: Dependency Audit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Composer audit
if: hashFiles('composer.lock') != ''
run: |
echo "=== Composer Security Audit ==="
if ! command -v composer &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli composer >/dev/null 2>&1
fi
composer audit --format=plain 2>&1 | tee /tmp/composer-audit.txt
RESULT=$?
if [ $RESULT -ne 0 ]; then
echo "::warning::Composer vulnerabilities found"
echo "composer_vulnerable=true" >> "$GITHUB_ENV"
else
echo "No known vulnerabilities in composer dependencies"
fi
- name: NPM audit
if: hashFiles('package-lock.json') != ''
run: |
echo "=== NPM Security Audit ==="
npm audit --production 2>&1 | tee /tmp/npm-audit.txt || true
if npm audit --production 2>&1 | grep -q "found 0 vulnerabilities"; then
echo "No known vulnerabilities in npm dependencies"
else
echo "::warning::NPM vulnerabilities found"
echo "npm_vulnerable=true" >> "$GITHUB_ENV"
fi
- name: Notify on vulnerabilities
if: env.composer_vulnerable == 'true' || env.npm_vulnerable == 'true'
run: |
REPO="${{ github.event.repository.name }}"
curl -sS \
-H "Title: ${REPO} has vulnerable dependencies" \
-H "Tags: lock,warning" \
-H "Priority: high" \
-d "Security audit found vulnerabilities. Review dependency updates." \
"${NTFY_URL}/${NTFY_TOPIC}" || true
-12
View File
@@ -1,12 +0,0 @@
# Test workflow to verify .mokogitea/ directory is discovered
name: Test .mokogitea workflows
on:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Verify .mokogitea
run: echo "This workflow ran from .mokogitea/workflows/ — feature works!"
-167
View File
@@ -1,167 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# BRIEF: Sync upstream Gitea bug fixes into MokoGitea issue tracker
name: Upstream Bug Sync
on:
schedule:
- cron: '0 8 * * *' # daily at 08:00 UTC
workflow_dispatch:
inputs:
days_back:
description: 'How many days back to scan (default: 7)'
required: false
default: '7'
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Sync upstream bugs
env:
GH_TOKEN: ${{ secrets.GH_MIRROR_TOKEN }}
MOKOGITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
MOKOGITEA_URL: https://code.mokoconsulting.tech
MOKOGITEA_REPO: MokoConsulting/MokoGitea
UPSTREAM_BRANCH: release/v1.26
DAYS_BACK: ${{ github.event.inputs.days_back || '7' }}
run: |
python3 << 'PYEOF'
import json, os, re, sys, urllib.parse, urllib.request
from datetime import datetime, timedelta, timezone
GH_TOKEN = os.environ["GH_TOKEN"]
MOKO_TOKEN = os.environ["MOKOGITEA_TOKEN"]
MOKO_URL = os.environ["MOKOGITEA_URL"]
MOKO_REPO = os.environ["MOKOGITEA_REPO"]
BRANCH = os.environ["UPSTREAM_BRANCH"]
DAYS = int(os.environ.get("DAYS_BACK", "7"))
# Label IDs in MokoGitea
LABELS = {
"type_bug": 5757, "upstream": 5758, "security": 5032,
"critical": 5018, "high": 5019, "medium": 5020, "low": 5021,
}
def gh_get(url):
req = urllib.request.Request(url, headers={
"Authorization": f"token {GH_TOKEN}",
"Accept": "application/vnd.github.v3+json",
})
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
def moko_get(path):
req = urllib.request.Request(f"{MOKO_URL}/api/v1/{path}", headers={
"Authorization": f"token {MOKO_TOKEN}",
})
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
def moko_post(path, data):
payload = json.dumps(data).encode()
req = urllib.request.Request(f"{MOKO_URL}/api/v1/{path}",
data=payload, method="POST", headers={
"Authorization": f"token {MOKO_TOKEN}",
"Content-Type": "application/json",
})
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
# ── Step 1: Find recently merged upstream PRs ──
since = (datetime.now(timezone.utc) - timedelta(days=DAYS)).strftime("%Y-%m-%dT%H:%M:%SZ")
query = f"repo:go-gitea/gitea is:pr is:merged base:{BRANCH} merged:>={since}"
encoded = urllib.parse.quote(query)
print(f"Scanning: {query}")
result = gh_get(f"https://api.github.com/search/issues?q={encoded}&per_page=100&sort=updated&order=desc")
total = result["total_count"]
print(f"Found {total} merged PRs in the last {DAYS} days")
if total == 0:
print("Nothing to sync.")
sys.exit(0)
# ── Step 2: Filter for bug/security fixes ──
bugs = []
for pr in result["items"]:
title = pr["title"]
label_names = [l["name"].lower() for l in pr.get("labels", [])]
is_fix = title.lower().startswith("fix")
is_security = any("security" in l for l in label_names) or "[security]" in title.lower()
is_bug = any("bug" in l for l in label_names)
if not (is_fix or is_security or is_bug):
continue
refs = re.findall(r"#(\d+)", title)
severity = "critical" if is_security and "[security]" in title.lower() else \
"high" if is_security else "medium"
bugs.append({
"number": pr["number"], "title": title, "url": pr["html_url"],
"severity": severity, "is_security": is_security, "refs": refs,
"merged": pr.get("pull_request", {}).get("merged_at", "")[:10],
})
print(f"Filtered to {len(bugs)} bug/security fixes")
if not bugs:
sys.exit(0)
# ── Step 3: Collect already-tracked PR numbers ──
tracked = set()
for state in ["open", "closed"]:
try:
issues = moko_get(f"repos/{MOKO_REPO}/issues?state={state}&type=issues&limit=50&labels=upstream")
for iss in issues:
text = (iss.get("body") or "") + " " + (iss.get("title") or "")
tracked.update(re.findall(r"(?:#|/pull/)(\d{4,})", text))
except Exception:
pass
print(f"Already tracked: {len(tracked)} upstream PRs")
# ── Step 4: Create issues for new bugs ──
created = skipped = errors = 0
for bug in bugs:
if any(r in tracked for r in bug["refs"]):
print(f" SKIP #{bug['number']}: {bug['title'][:55]} (tracked)")
skipped += 1
continue
labels = [LABELS["type_bug"], LABELS["upstream"], LABELS[bug["severity"]]]
if bug["is_security"]:
labels.append(LABELS["security"])
body = (
f"## Summary\n\n"
f"Upstream bug fix merged into `{BRANCH}`.\n\n"
f"## Upstream Reference\n\n"
f"- PR: {bug['url']}\n"
f"- Merged: {bug['merged']}\n"
f"- Branch: {BRANCH}\n\n"
f"## Severity: {bug['severity'].title()}"
f"{' (security)' if bug['is_security'] else ''}\n\n"
f"## Action\n\n"
f"Cherry-pick from upstream `{BRANCH}` branch.\n\n"
f"---\n"
f"*Auto-created by upstream-bug-sync workflow*\n"
f"*Authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>*"
)
try:
iss = moko_post(f"repos/{MOKO_REPO}/issues", {
"title": bug["title"], "body": body, "labels": labels,
})
print(f" CREATED #{iss['number']}: {bug['title'][:55]}")
created += 1
except Exception as e:
print(f" ERROR #{bug['number']}: {e}")
errors += 1
print(f"\n=== Done: {created} created, {skipped} skipped, {errors} errors ===")
PYEOF
-6
View File
@@ -1,7 +1 @@
audit=false
fund=false
update-notifier=false
save-exact=true
auto-install-peers=true
dedupe-peer-dependents=false
enable-pre-post-scripts=true
+25
View File
@@ -0,0 +1,25 @@
ignoreGeneratedHeader = false
severity = "warning"
confidence = 0.8
errorCode = 1
warningCode = 1
[rule.blank-imports]
[rule.context-as-argument]
[rule.context-keys-type]
[rule.dot-imports]
[rule.error-return]
[rule.error-strings]
[rule.error-naming]
[rule.exported]
[rule.if-return]
[rule.increment-decrement]
[rule.var-naming]
[rule.var-declaration]
[rule.package-comments]
[rule.range]
[rule.receiver-naming]
[rule.time-naming]
[rule.unexported-return]
[rule.indent-error-flow]
[rule.errorf]
-12
View File
@@ -1,12 +0,0 @@
extends: [[spectral:oas, all]]
rules:
info-contact: off
oas2-api-host: off
oas2-parameter-description: off
oas2-schema: off
oas2-valid-schema-example: off
openapi-tags: off
operation-description: off
operation-singular-tag: off
operation-tag-defined: off
+11
View File
@@ -0,0 +1,11 @@
extends: stylelint-config-standard
rules:
block-closing-brace-empty-line-before: null
color-hex-length: null
comment-empty-line-before: null
declaration-empty-line-before: null
indentation: 4
no-descending-specificity: null
rule-empty-line-before: null
selector-pseudo-element-colon-notation: null
-42
View File
@@ -1,42 +0,0 @@
extends: default
rules:
braces:
min-spaces-inside: 0
max-spaces-inside: 1
min-spaces-inside-empty: 0
max-spaces-inside-empty: 0
brackets:
min-spaces-inside: 0
max-spaces-inside: 1
min-spaces-inside-empty: 0
max-spaces-inside-empty: 0
comments:
require-starting-space: true
ignore-shebangs: true
min-spaces-from-content: 1
comments-indentation:
level: error
document-start: disable
document-end:
present: false
empty-lines:
max: 1
indentation:
spaces: 2
line-length: disable
truthy:
allowed-values: ["true", "false", "on", "off"]
ignore: |
.venv
node_modules
+13 -20
View File
@@ -1,7 +1,6 @@
# GNU makefile proxy script for BSD make
#
# Written and maintained by Mahmoud Al-Qudsi <mqudsi@neosmart.net>
# Copyright NeoSmart Technologies <https://neosmart.net/> 2014-2019
# Copyright NeoSmart Technologies <https://neosmart.net/> 2014-2018
# Obtain updates from <https://github.com/neosmart/gmake-proxy>
#
# Redistribution and use in source and binary forms, with or without
@@ -27,32 +26,26 @@
JARG =
GMAKE = "gmake"
# When gmake is called from another make instance, -w is automatically added
# which causes extraneous messages about directory changes to be emitted.
# Running with --no-print-directory silences these messages.
#When gmake is called from another make instance, -w is automatically added
#which causes extraneous messages about directory changes to be emitted.
#--no-print-directory silences these messages.
GARGS = "--no-print-directory"
.if "$(.MAKE.JOBS)" != ""
JARG = -j$(.MAKE.JOBS)
JARG = -j$(.MAKE.JOBS)
.endif
# bmake prefers out-of-source builds and tries to cd into ./obj (among others)
# where possible. GNU Make doesn't, so override that value.
#by default bmake will cd into ./obj first
.OBJDIR: ./
# The GNU convention is to use the lowercased `prefix` variable/macro to
# specify the installation directory. Humor them.
GPREFIX =
.if defined(PREFIX) && ! defined(prefix)
GPREFIX = 'prefix = "$(PREFIX)"'
.endif
.BEGIN: .SILENT
which $(GMAKE) || (printf "Error: GNU Make is required!\n\n" 1>&2 && false)
.PHONY: FRC
$(.TARGETS): FRC
$(GMAKE) $(GPREFIX) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG)
$(GMAKE) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG)
.DONE .DEFAULT: .SILENT
$(GMAKE) $(GPREFIX) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG)
$(GMAKE) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG)
.ERROR: .SILENT
if ! which $(GMAKE) > /dev/null; then \
echo "GNU Make is required!"; \
fi
File diff suppressed because it is too large Load Diff
+2368 -207
View File
File diff suppressed because it is too large Load Diff
-96
View File
@@ -1,96 +0,0 @@
# Gitea Community Code of Conduct
## About
Online communities include people from many different backgrounds. The Gitea contributors are committed to providing a friendly, safe and welcoming environment for all, regardless of gender identity and expression, sexual orientation, disabilities, neurodiversity, physical appearance, body size, ethnicity, nationality, race, age, religion, or similar personal characteristics.
The first goal of the Code of Conduct is to specify a baseline standard of behavior so that people with different social values and communication styles can talk about Gitea effectively, productively, and respectfully.
The second goal is to provide a mechanism for resolving conflicts in the community when they arise.
The third goal of the Code of Conduct is to make our community welcoming to people from different backgrounds. Diversity is critical to the project; for Gitea to be successful, it needs contributors and users from all backgrounds.
We believe that healthy debate and disagreement are essential to a healthy project and community. However, it is never ok to be disrespectful. We value diverse opinions, but we value respectful behavior more.
## Community values
These are the values to which people in the Gitea community should aspire.
- **Be friendly and welcoming.**
- **Be patient.**
- Remember that people have varying communication styles and that not everyone is using their native language. (Meaning and tone can be lost in translation.)
- **Be thoughtful.**
- Productive communication requires effort. Think about how your words will be interpreted.
- Remember that sometimes it is best to refrain entirely from commenting.
- **Be respectful.**
- In particular, respect differences of opinion.
- **Be charitable.**
- Interpret the arguments of others in good faith, do not seek to disagree.
- When we do disagree, try to understand why.
- **Be constructive.**
- Avoid derailing: stay on topic; if you want to talk about something else, start a new conversation.
- Avoid unconstructive criticism: don't merely decry the current state of affairs; offer—or at least solicit—suggestions as to how things may be improved.
- Avoid snarking (pithy, unproductive, sniping comments).
- Avoid discussing potentially offensive or sensitive issues; this all too often leads to unnecessary conflict.
- Avoid microaggressions (brief and commonplace verbal, behavioral and environmental indignities that communicate hostile, derogatory or negative slights and insults to a person or group).
- **Be responsible.**
- What you say and do matters. Take responsibility for your words and actions, including their consequences, whether intended or otherwise.
People are complicated. You should expect to be misunderstood and to misunderstand others; when this inevitably occurs, resist the urge to be defensive or assign blame. Try not to take offense where no offense was intended. Give people the benefit of the doubt. Even if the intent was to provoke, do not rise to it. It is the responsibility of all parties to de-escalate conflict when it arises.
## Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
### Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject: comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, as well as to ban (temporarily or permanently) any contributor for behaviors that they deem inappropriate, threatening, offensive, or harmful.
### Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
This Code of Conduct also applies outside the project spaces when the Project Stewards have a reasonable belief that an individuals behavior may have a negative impact on the project or its community.
### Conflict Resolution
We do not believe that all conflict is bad; healthy debate and disagreement often yield positive results. However, it is never okay to be disrespectful or to engage in behavior that violates the projects code of conduct.
If you see someone violating the code of conduct, you are encouraged to address the behavior directly with those involved. Many issues can be resolved quickly and easily, and this gives people more control over the outcome of their dispute. If you are unable to resolve the matter for any reason, or if the behavior is threatening or harassing, report it. We are dedicated to providing an environment where participants feel welcome and safe.
Reports should be directed to the Gitea Project Stewards at conduct@gitea.com. It is the Project Stewards duty to receive and address reported violations of the code of conduct. They will then work with a committee consisting of representatives from the technical-oversight-committee.
We will investigate every complaint, but you may not receive a direct response. We will use our discretion in determining when and how to follow up on reported incidents, which may range from not taking action to permanent expulsion from the project and project-sponsored spaces. Under normal circumstances, we will notify the accused of the report and provide them an opportunity to discuss it before any action is taken. If there is a consensus between maintainers that such an endeavor would be useless (i.e. in case of an obvious spammer), we reserve the right to take action without notifying the accused first. The identity of the reporter will be omitted from the details of the report supplied to the accused. In potentially harmful situations, such as ongoing harassment or threats to anyones safety, we may take action without notice.
### Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
## Summary
- Treat everyone with respect and kindness.
- Be thoughtful in how you communicate.
- Dont be destructive or inflammatory.
- If you encounter an issue, please mail conduct@gitea.com.

Some files were not shown because too many files have changed in this diff Show More