Compare commits

..

1 Commits

Author SHA1 Message Date
Lunny Xiao 4e62d158cb docs(image): document Docker image naming mismatch
Add OCI title/description labels to the Dockerfile, note the
docker.gitea.com/gitea-mcp-server vs gitea-mcp naming mismatch in each
README with a link to a new docs/adr note recording the maintainers'
preferred gitea/mcp-server direction from issue #181, and add a test
that keeps the README image references consistent.

Co-Authored-By: Codet <codet@commitgo.dev> (GPT-5-Codex)
2026-08-23 23:28:23 -07:00
8 changed files with 71 additions and 118 deletions
+2
View File
@@ -31,5 +31,7 @@ USER nonroot:nonroot
LABEL org.opencontainers.image.version="${VERSION}" LABEL org.opencontainers.image.version="${VERSION}"
LABEL org.opencontainers.image.source="https://gitea.com/gitea/gitea-mcp" LABEL org.opencontainers.image.source="https://gitea.com/gitea/gitea-mcp"
LABEL org.opencontainers.image.title="Gitea MCP Server"
LABEL org.opencontainers.image.description="Model Context Protocol server connecting Gitea instances to MCP clients, published as docker.gitea.com/gitea-mcp-server."
CMD ["/app/gitea-mcp"] CMD ["/app/gitea-mcp"]
+2
View File
@@ -10,6 +10,8 @@
Download a binary from the [releases page](https://gitea.com/gitea/gitea-mcp/releases) and put it in your `PATH`, use the `docker.gitea.com/gitea-mcp-server` image, or build from source into `$GOPATH/bin` with `make` and Go 1.26 or later: Download a binary from the [releases page](https://gitea.com/gitea/gitea-mcp/releases) and put it in your `PATH`, use the `docker.gitea.com/gitea-mcp-server` image, or build from source into `$GOPATH/bin` with `make` and Go 1.26 or later:
> The published Docker image is `docker.gitea.com/gitea-mcp-server`, which does not match the `gitea-mcp` repository name. This is a known mismatch, tracked in [issue #181](https://gitea.com/gitea/gitea-mcp/issues/181); the image name stays as-is for now to avoid breaking existing installs. See [docs/adr/docker-image-naming.md](docs/adr/docker-image-naming.md) for details.
```bash ```bash
git clone https://gitea.com/gitea/gitea-mcp.git git clone https://gitea.com/gitea/gitea-mcp.git
cd gitea-mcp cd gitea-mcp
+2
View File
@@ -10,6 +10,8 @@
可从 [发布页面](https://gitea.com/gitea/gitea-mcp/releases) 下载二进制文件并放入 `PATH`,或使用 `docker.gitea.com/gitea-mcp-server` 镜像,也可用 `make` 和 Go 1.26 及以上从源码构建到 `$GOPATH/bin` 可从 [发布页面](https://gitea.com/gitea/gitea-mcp/releases) 下载二进制文件并放入 `PATH`,或使用 `docker.gitea.com/gitea-mcp-server` 镜像,也可用 `make` 和 Go 1.26 及以上从源码构建到 `$GOPATH/bin`
> 已发布的 Docker 镜像名为 `docker.gitea.com/gitea-mcp-server`,与 `gitea-mcp` 仓库名不一致。这是 [issue #181](https://gitea.com/gitea/gitea-mcp/issues/181) 中记录的已知不一致,为避免破坏现有安装,镜像名暂不改动。详见 [docs/adr/docker-image-naming.md](docs/adr/docker-image-naming.md)。
```bash ```bash
git clone https://gitea.com/gitea/gitea-mcp.git git clone https://gitea.com/gitea/gitea-mcp.git
cd gitea-mcp cd gitea-mcp
+2
View File
@@ -10,6 +10,8 @@
可從 [發布頁面](https://gitea.com/gitea/gitea-mcp/releases) 下載二進位檔並放入 `PATH`,或使用 `docker.gitea.com/gitea-mcp-server` 映像檔,也可用 `make` 與 Go 1.26 以上從原始碼建置到 `$GOPATH/bin` 可從 [發布頁面](https://gitea.com/gitea/gitea-mcp/releases) 下載二進位檔並放入 `PATH`,或使用 `docker.gitea.com/gitea-mcp-server` 映像檔,也可用 `make` 與 Go 1.26 以上從原始碼建置到 `$GOPATH/bin`
> 已發布的 Docker 映像檔名稱為 `docker.gitea.com/gitea-mcp-server`,與 `gitea-mcp` 倉庫名稱不一致。這是 [issue #181](https://gitea.com/gitea/gitea-mcp/issues/181) 中記錄的已知不一致,為避免破壞現有安裝,映像檔名稱暫不變更。詳見 [docs/adr/docker-image-naming.md](docs/adr/docker-image-naming.md)。
```bash ```bash
git clone https://gitea.com/gitea/gitea-mcp.git git clone https://gitea.com/gitea/gitea-mcp.git
cd gitea-mcp cd gitea-mcp
+29
View File
@@ -0,0 +1,29 @@
# Docker image naming mismatch (issue #181)
## Current state
The project repository is `gitea.com/gitea/gitea-mcp`, but the Docker image is
published as `docker.gitea.com/gitea-mcp-server`. This mismatch was raised in
[issue #181](https://gitea.com/gitea/gitea-mcp/issues/181).
## Maintainers' direction
Per the issue discussion, the maintainers' preferred long-term name is
`gitea/mcp-server`, matching the naming scheme used by `gitea/act_runner`
(published as a `runner`-style short name) and by comparable projects such as
`github/github-mcp-server`. The name `mcp-server` was preferred over
`gitea-mcp-server` because the repository is primarily run as a local client
process, not a traditional long-running server.
## Decision
No rename has happened yet; the discussion was explicitly left open in the
issue. The current image name, `docker.gitea.com/gitea-mcp-server`, remains in
use so that existing user configurations and documentation keep working.
`org.opencontainers.image.title` and `org.opencontainers.image.description`
labels were added to the `Dockerfile` to make the image self-describing
regardless of the tag it is published under.
If the image is renamed in the future, update the `docker.gitea.com/gitea-mcp-server`
references across `README.md`, `README.zh-cn.md` and `README.zh-tw.md` together
with this document.
+2 -57
View File
@@ -1,10 +1,7 @@
package main package main
import ( import (
"path"
"runtime/debug" "runtime/debug"
"strconv"
"strings"
"gitea.com/gitea/gitea-mcp/cmd" "gitea.com/gitea/gitea-mcp/cmd"
"gitea.com/gitea/gitea-mcp/pkg/flag" "gitea.com/gitea/gitea-mcp/pkg/flag"
@@ -14,65 +11,13 @@ var Version = "dev"
func init() { func init() {
if Version == "dev" { if Version == "dev" {
if info, ok := debug.ReadBuildInfo(); ok { if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "" && info.Main.Version != "(devel)" {
Version = resolveVersion(Version, info) Version = info.Main.Version
} }
} }
flag.Version = Version flag.Version = Version
} }
// resolveVersion returns the version reported by debug.ReadBuildInfo when its
// major version matches the major version encoded in the module path (e.g.
// "/v2" suffix). Otherwise it falls back to devVersion, since Go's module
// versioning rules make a mismatched major version untrustworthy (see #231).
func resolveVersion(devVersion string, info *debug.BuildInfo) string {
if info == nil {
return devVersion
}
buildVersion := info.Main.Version
if buildVersion == "" || buildVersion == "(devel)" {
return devVersion
}
buildMajor := majorVersionOf(buildVersion)
pathMajor := majorVersionFromModulePath(info.Main.Path)
if buildMajor != pathMajor {
return devVersion
}
return buildVersion
}
// majorVersionOf extracts the numeric major version from a semver-like
// string such as "v1.2.3", returning 0 if it cannot be parsed.
func majorVersionOf(version string) int {
version = strings.TrimPrefix(version, "v")
dot := strings.IndexByte(version, '.')
if dot >= 0 {
version = version[:dot]
}
major, err := strconv.Atoi(version)
if err != nil {
return 0
}
return major
}
// majorVersionFromModulePath returns the major version encoded in a module
// path's "/vN" suffix, or 1 if the module path has no such suffix (as is the
// case for v0 and v1 modules).
func majorVersionFromModulePath(modulePath string) int {
suffix := path.Base(modulePath)
if len(suffix) < 2 || suffix[0] != 'v' {
return 1
}
major, err := strconv.Atoi(suffix[1:])
if err != nil {
return 1
}
return major
}
func main() { func main() {
cmd.Execute() cmd.Execute()
} }
-61
View File
@@ -1,61 +0,0 @@
package main
import (
"runtime/debug"
"testing"
)
func TestResolveVersion(t *testing.T) {
cases := []struct {
name string
dev string
info *debug.BuildInfo
want string
}{
{
name: "nil build info falls back to dev version",
dev: "dev",
info: nil,
want: "dev",
},
{
name: "devel version falls back to dev version",
dev: "dev",
info: &debug.BuildInfo{Main: debug.Module{Path: "gitea.com/gitea/gitea-mcp", Version: "(devel)"}},
want: "dev",
},
{
name: "empty version falls back to dev version",
dev: "dev",
info: &debug.BuildInfo{Main: debug.Module{Path: "gitea.com/gitea/gitea-mcp", Version: ""}},
want: "dev",
},
{
name: "v1 version accepted for module path without major suffix",
dev: "dev",
info: &debug.BuildInfo{Main: debug.Module{Path: "gitea.com/gitea/gitea-mcp", Version: "v1.2.3"}},
want: "v1.2.3",
},
{
name: "v2 version rejected when module path has no /v2 suffix",
dev: "dev",
info: &debug.BuildInfo{Main: debug.Module{Path: "gitea.com/gitea/gitea-mcp", Version: "v2.0.0"}},
want: "dev",
},
{
name: "v2 version accepted when module path has /v2 suffix",
dev: "dev",
info: &debug.BuildInfo{Main: debug.Module{Path: "gitea.com/gitea/gitea-mcp/v2", Version: "v2.0.0"}},
want: "v2.0.0",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := resolveVersion(tc.dev, tc.info)
if got != tc.want {
t.Errorf("resolveVersion(%q, %+v) = %q, want %q", tc.dev, tc.info, got, tc.want)
}
})
}
}
+32
View File
@@ -22,6 +22,38 @@ var readmeAccessLabels = map[string]map[string]string{
"../README.zh-tw.md": {"讀取": "read", "寫入": "write"}, "../README.zh-tw.md": {"讀取": "read", "寫入": "write"},
} }
// publishedDockerImage is the image name published for this project. See
// docs/adr/docker-image-naming.md for why it differs from the repository name.
const publishedDockerImage = "docker.gitea.com/gitea-mcp-server"
// dockerImageReference matches any docker.gitea.com/<name> image reference so
// stray typos or partial renames in the README files can be caught.
var dockerImageReference = regexp.MustCompile(`docker\.gitea\.com/[a-zA-Z0-9._-]+`)
// TestReadmeDockerImageReferencesAreConsistent ensures every README mentions
// the same, currently published Docker image name. A partial rename, where
// one example is updated but another is missed, would otherwise leave users
// copying a command that pulls a nonexistent image.
func TestReadmeDockerImageReferencesAreConsistent(t *testing.T) {
for _, path := range []string{"../README.md", "../README.zh-cn.md", "../README.zh-tw.md"} {
t.Run(filepath.Base(path), func(t *testing.T) {
content, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
references := dockerImageReference.FindAllString(string(content), -1)
if len(references) == 0 {
t.Fatalf("no docker.gitea.com image reference found in %s", path)
}
for _, ref := range references {
if ref != publishedDockerImage {
t.Errorf("found image reference %q in %s, want %q", ref, path, publishedDockerImage)
}
}
})
}
}
// toolInfo is what TestReadmeToolTables tracks per tool, both as registered // toolInfo is what TestReadmeToolTables tracks per tool, both as registered
// in code and as documented in a README, so the two can be compared. // in code and as documented in a README, so the two can be compared.
type toolInfo struct { type toolInfo struct {