Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add simple update checker to Gitea #17212

Merged
merged 19 commits into from
Oct 16, 2021
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1925,6 +1925,19 @@ PATH =
;SCHEDULE = @every 168h
;OLDER_THAN = 8760h

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Check for new Gitea versions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[cron.update_checker]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;ENABLED = false
;RUN_AT_START = false
;ENABLE_SUCCESS_NOTICE = false
;SCHEDULE = @every 168h
;HTTP_ENDPOINT = https://dl.gitea.io/gitea/version.json

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Git Operation timeout in seconds
Expand Down
13 changes: 10 additions & 3 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ or any corresponding location. When installing from a distribution, this will
typically be found at `/etc/gitea/conf/app.ini`.

The defaults provided here are best-effort (not built automatically). They are
accurately recorded in [app.example.ini](https://github.com/go-gitea/gitea/blob/master/custom/conf/app.example.ini)
(s/master/\<tag|release\>). Any string in the format `%(X)s` is a feature powered
accurately recorded in [app.example.ini](https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini)
(s/main/\<tag|release\>). Any string in the format `%(X)s` is a feature powered
by [ini](https://github.com/go-ini/ini/#recursive-values), for reading values recursively.

Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
Expand Down Expand Up @@ -824,9 +824,16 @@ NB: You must have `DISABLE_ROUTER_LOG` set to `false` for this option to take ef
- `ENABLED`: **false**: Enable service.
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices.
- `SCHEDULE`: **@every 128h**: Cron syntax for scheduling a work, e.g. `@every 128h`.
- `SCHEDULE`: **@every 168h**: Cron syntax to set how often to check.
- `OLDER_THAN`: **@every 8760h**: any action older than this expression will be deleted from database, suggest using `8760h` (1 year) because that's the max length of heatmap.

#### Cron - Check for new Gitea versions ('cron.update_checker')
- `ENABLED`: **false**: Enable service.
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `ENABLE_SUCCESS_NOTICE`: **true**: Set to false to switch off success notices.
- `SCHEDULE`: **@every 168h**: Cron syntax for scheduling a work, e.g. `@every 168h`.
- `HTTP_ENDPOINT`: **https://dl.gitea.io/gitea/version.json**: the endpoint that Gitea will check for newer versions

## Git (`git`)

- `PATH`: **""**: The path of git executable. If empty, Gitea searches through the PATH environment.
Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ var migrations = []Migration{
NewMigration("Add renamed_branch table", addRenamedBranchTable),
// v198 -> v199
NewMigration("Add issue content history table", addTableIssueContentHistory),
// v199 -> v200
NewMigration("Add remote version table", addRemoteVersionTable),
}

// GetCurrentDBVersion returns the current db version
Expand Down
23 changes: 23 additions & 0 deletions models/migrations/v199.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
"fmt"

"xorm.io/xorm"
)

func addRemoteVersionTable(x *xorm.Engine) error {
type RemoteVersion struct {
ID int64 `xorm:"pk autoincr"`
Version string `xorm:"VARCHAR(50)"`
}

if err := x.Sync2(new(RemoteVersion)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
return nil
}
121 changes: 121 additions & 0 deletions models/update_checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/setting"

"github.com/hashicorp/go-version"
)

// RemoteVersion stores the remote version from the JSON endpoint
type RemoteVersion struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think store the version into database is unnecessary. Just send http request every check is better.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are storing in the database because we don't want to DDOS ourselves when everyone goes to admin dashboard. Even if we get beefy servers, the user dashboard will always be slower because an HTTP check would need to happen. By storing it in the DB we will eventually be able to add a check to send out an email to admins when a new update happens.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On option is storing the remote version into Gitea local cache in memory.

Another option is using database (this PR).

Both are fine to me.

ID int64 `xorm:"pk autoincr"`
Version string `xorm:"VARCHAR(50)"`
}

func init() {
db.RegisterModel(new(RemoteVersion))
}

// GiteaUpdateChecker returns error when new version of Gitea is available
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
func GiteaUpdateChecker(httpEndpoint string) error {
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: proxy.Proxy(),
},
}

req, err := http.NewRequest("GET", httpEndpoint, nil)
if err != nil {
return err
}
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}

type v struct {
Latest struct {
Version string `json:"version"`
} `json:"latest"`
}
ver := v{}
err = json.Unmarshal(body, &ver)
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

return UpdateRemoteVersion(ver.Latest.Version)

}

// UpdateRemoteVersion updates the latest available version of Gitea
func UpdateRemoteVersion(version string) (err error) {
sess := db.NewSession(db.DefaultContext)
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}

currentVersion := &RemoteVersion{ID: 1}
has, err := sess.Get(currentVersion)
if err != nil {
return fmt.Errorf("get: %v", err)
} else if !has {
currentVersion.ID = 1
currentVersion.Version = version

if _, err = sess.InsertOne(currentVersion); err != nil {
return fmt.Errorf("insert: %v", err)
}
return nil
}

if _, err = sess.Update(&RemoteVersion{ID: 1, Version: version}); err != nil {
return err
}

return sess.Commit()
}

// GetRemoteVersion returns the current remote version (or currently installed verson if fail to fetch from DB)
func GetRemoteVersion() string {
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
e := db.GetEngine(db.DefaultContext)
v := &RemoteVersion{ID: 1}
_, err := e.Get(&v)
if err != nil {
// return current version if fail to fetch from DB
return setting.AppVer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really dislike the idea to return current version if there is a failure. It breaks the meaning the the function GetRemoteVersion. If I read code v = GetRemoteVersion(), I won't expect v is the current version. 😂

Anyway, if you feel it's OK, since it doesn't do harm to the logic .....

}
return v.Version
}

// GetNeedUpdate returns true whether a newer version of Gitea is available
func GetNeedUpdate() bool {
curVer, err := version.NewVersion(setting.AppVer)
if err != nil {
// return false to fail silently
return false
}
remoteVer, err := version.NewVersion(GetRemoteVersion())
if err != nil {
// return false to fail silently
return false
}
return curVer.LessThan(remoteVer)
}
19 changes: 19 additions & 0 deletions modules/cron/tasks_extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,24 @@ func registerDeleteOldActions() {
})
}

func registerUpdateGiteaChecker() {
type UpdateCheckerConfig struct {
BaseConfig
HTTPEndpoint string
}
RegisterTaskFatal("update_checker", &UpdateCheckerConfig{
BaseConfig: BaseConfig{
Enabled: true,
RunAtStart: false,
Schedule: "@every 168h",
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
},
HTTPEndpoint: "https://dl.gitea.io/gitea/version.json",
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
}, func(ctx context.Context, _ *models.User, config Config) error {
updateCheckerConfig := config.(*UpdateCheckerConfig)
return models.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint)
})
}

func initExtendedTasks() {
registerDeleteInactiveUsers()
registerDeleteRepositoryArchives()
Expand All @@ -142,4 +160,5 @@ func initExtendedTasks() {
registerDeleteMissingRepositories()
registerRemoveRandomAvatars()
registerDeleteOldActions()
registerUpdateGiteaChecker()
}
2 changes: 2 additions & 0 deletions routers/web/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ func Dashboard(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminDashboard"] = true
ctx.Data["Stats"] = models.GetStatistic()
ctx.Data["NeedUpdate"] = models.GetNeedUpdate()
ctx.Data["RemoteVersion"] = models.GetRemoteVersion()
// FIXME: update periodically
updateSystemStatus()
ctx.Data["SysStatus"] = sysStatus
Expand Down
5 changes: 5 additions & 0 deletions templates/admin/dashboard.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
{{template "admin/navbar" .}}
<div class="ui container">
{{template "base/alert" .}}
{{if .NeedUpdate}}
<div class="ui negative message flash-error">
<p>"Gitea {{.RemoteVersion | Str2html}} is now available, you are running {{.AppVer | Str2html}}. Check the <a href="https://blog.gitea.io">blog</a> for more details.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n?

</div>
{{end}}
<h4 class="ui top attached header">
{{.i18n.Tr "admin.dashboard.statistic"}}
</h4>
Expand Down