From 290ad8da5dc40f1d3cfe2b361ee5e62cb831bd78 Mon Sep 17 00:00:00 2001 From: Gronod Date: Tue, 15 Sep 2026 13:58:13 +0100 Subject: [PATCH] chore(release): publish to self-hosted gitea instance Repoint image pushes to git.i3omb.com/gronod/gitea-mcp-server using GITHUB_TOKEN auth, drop the Cloudflare R2 mirror, and update the docs and OCI source label to match. Assisted-by: Devin:SWE-2 High --- .gitea/workflows/release-nightly.yml | 14 ++-- .gitea/workflows/release-tag.yml | 41 +++--------- .goreleaser.yaml | 4 +- Dockerfile | 2 +- README.md | 10 +-- README.zh-cn.md | 10 +-- README.zh-tw.md | 10 +-- scripts/upload-r2.sh | 96 ---------------------------- 8 files changed, 33 insertions(+), 154 deletions(-) delete mode 100755 scripts/upload-r2.sh diff --git a/.gitea/workflows/release-nightly.yml b/.gitea/workflows/release-nightly.yml index 0a13c3c..92e8d5b 100644 --- a/.gitea/workflows/release-nightly.yml +++ b/.gitea/workflows/release-nightly.yml @@ -10,8 +10,8 @@ jobs: release-image: runs-on: ubuntu-latest env: - DOCKER_ORG: gitea - DOCKER_LATEST: nightly + REGISTRY: git.i3omb.com + IMAGE_NAME: gronod/gitea-mcp-server steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -24,16 +24,16 @@ jobs: - name: Set up Docker BuildX uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4 - - name: Login to DockerHub + - name: Login to container registry uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 with: - username: ${{ secrets.DOCKER_USER }} - password: ${{ secrets.DOCKER_TOKEN }} + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Get Meta id: meta run: | - echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT echo REPO_VERSION=$(git describe --tags --always | sed 's/-/+/' | sed 's/^v//') >> $GITHUB_OUTPUT - name: Build and push @@ -46,6 +46,6 @@ jobs: linux/arm64 push: true tags: | - ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ env.DOCKER_LATEST }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly build-args: | VERSION=${{ steps.meta.outputs.REPO_VERSION }} diff --git a/.gitea/workflows/release-tag.yml b/.gitea/workflows/release-tag.yml index 649b10b..2fd8c03 100644 --- a/.gitea/workflows/release-tag.yml +++ b/.gitea/workflows/release-tag.yml @@ -13,16 +13,6 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: fetch-depth: 0 - # The R2 upload only runs after goreleaser has already published - # the Gitea release, so fail early instead if the secrets are - # missing. - - name: Check R2 configuration - run: sh scripts/upload-r2.sh --check-config - env: - R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }} - R2_BUCKET: ${{ secrets.R2_BUCKET }} - R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} - R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7 with: @@ -35,27 +25,12 @@ jobs: env: GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} GORELEASER_FORCE_TOKEN: "gitea" - # goreleaser `publishers:` is a Pro-only feature, so the release - # artifacts are mirrored to Cloudflare R2 here instead. - - name: Upload binaries to Cloudflare R2 - env: - R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }} - R2_BUCKET: ${{ secrets.R2_BUCKET }} - R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} - R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} - VERSION: ${{ github.ref_name }} - run: | - for f in dist/*.tar.gz dist/*.zip dist/checksums.txt; do - [ -f "$f" ] || continue - echo "uploading $f" - sh scripts/upload-r2.sh "$f" "gitea-mcp/${VERSION#v}/$(basename "$f")" - done release-image: runs-on: ubuntu-latest env: - DOCKER_ORG: gitea - DOCKER_LATEST: latest + REGISTRY: git.i3omb.com + IMAGE_NAME: gronod/gitea-mcp-server steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 @@ -68,16 +43,16 @@ jobs: - name: Set up Docker BuildX uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4 - - name: Login to DockerHub + - name: Login to container registry uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 with: - username: ${{ secrets.DOCKER_USER }} - password: ${{ secrets.DOCKER_TOKEN }} + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Get Meta id: meta run: | - echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT echo REPO_VERSION=${GITHUB_REF_NAME#v} >> $GITHUB_OUTPUT - name: Build and push @@ -92,5 +67,5 @@ jobs: build-args: | VERSION=${{ steps.meta.outputs.REPO_VERSION }} tags: | - ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ steps.meta.outputs.REPO_VERSION }} - ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}-server:${{ env.DOCKER_LATEST }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.REPO_VERSION }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 72da5ac..b6bc04d 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -71,6 +71,6 @@ release: Released by [GoReleaser](https://github.com/goreleaser/goreleaser). gitea_urls: - api: https://gitea.com/api/v1 - download: https://gitea.com + api: https://git.i3omb.com/api/v1 + download: https://git.i3omb.com force_token: gitea diff --git a/Dockerfile b/Dockerfile index 01c0fdb..e59ea09 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,7 @@ COPY --from=builder --chown=nonroot:nonroot /app/gitea-mcp . USER nonroot:nonroot LABEL org.opencontainers.image.version="${VERSION}" -LABEL org.opencontainers.image.source="https://gitea.com/gitea/gitea-mcp" +LABEL org.opencontainers.image.source="https://git.i3omb.com/gronod/gitea-mcp" HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD ["/app/gitea-mcp", "-healthcheck"] || exit 1 diff --git a/README.md b/README.md index d31566f..c9194cd 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ **Gitea MCP Server** connects a [Gitea](https://about.gitea.com) instance to [Model Context Protocol](https://modelcontextprotocol.io) clients, so repositories, issues, pull requests and more can be browsed and managed from an MCP-compatible chat interface. -[![Install with Docker in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22docker.gitea.com/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}) [![Install with Docker in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22docker.gitea.com/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}&quality=insiders) +[![Install with Docker in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22git.i3omb.com/gronod/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}) [![Install with Docker in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22git.i3omb.com/gronod/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}&quality=insiders) ## Installation -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.27 or later: +Download a binary from the [releases page](https://git.i3omb.com/gronod/gitea-mcp/releases) and put it in your `PATH`, use the `git.i3omb.com/gronod/gitea-mcp-server` image, or build from source into `$GOPATH/bin` with `make` and Go 1.27 or later: ```bash -git clone https://gitea.com/gitea/gitea-mcp.git +git clone https://git.i3omb.com/gronod/gitea-mcp.git cd gitea-mcp make install ``` @@ -57,7 +57,7 @@ Use the install buttons at the top of this README, or add the block below to you "servers": { "gitea-mcp": { "command": "docker", - "args": ["run", "-i", "--rm", "-e", "GITEA_ACCESS_TOKEN", "docker.gitea.com/gitea-mcp-server"], + "args": ["run", "-i", "--rm", "-e", "GITEA_ACCESS_TOKEN", "git.i3omb.com/gronod/gitea-mcp-server"], "env": { "GITEA_ACCESS_TOKEN": "${input:gitea_token}" } @@ -93,7 +93,7 @@ Add the following to `~/.vibe/config.toml`: name = "gitea" transport = "stdio" command = "docker" -args = ["run", "--rm", "-i", "-e", "GITEA_ACCESS_TOKEN", "-e", "GITEA_HOST", "docker.gitea.com/gitea-mcp-server"] +args = ["run", "--rm", "-i", "-e", "GITEA_ACCESS_TOKEN", "-e", "GITEA_HOST", "git.i3omb.com/gronod/gitea-mcp-server"] [mcp_servers.env] GITEA_ACCESS_TOKEN = "TOKEN" diff --git a/README.zh-cn.md b/README.zh-cn.md index 3bf0e33..f650583 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -4,14 +4,14 @@ **Gitea MCP 服务器** 将 [Gitea](https://about.gitea.com) 实例接入 [Model Context Protocol](https://modelcontextprotocol.io) 客户端,让仓库、问题、拉取请求等都能在兼容 MCP 的聊天界面中浏览和管理。 -[![在 VS Code 中使用 Docker 安装](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22docker.gitea.com/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}) [![在 VS Code Insiders 中使用 Docker 安装](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22docker.gitea.com/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}&quality=insiders) +[![在 VS Code 中使用 Docker 安装](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22git.i3omb.com/gronod/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}) [![在 VS Code Insiders 中使用 Docker 安装](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22git.i3omb.com/gronod/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}&quality=insiders) ## 安装 -可从 [发布页面](https://gitea.com/gitea/gitea-mcp/releases) 下载二进制文件并放入 `PATH`,或使用 `docker.gitea.com/gitea-mcp-server` 镜像,也可用 `make` 和 Go 1.27 及以上从源码构建到 `$GOPATH/bin`: +可从 [发布页面](https://git.i3omb.com/gronod/gitea-mcp/releases) 下载二进制文件并放入 `PATH`,或使用 `git.i3omb.com/gronod/gitea-mcp-server` 镜像,也可用 `make` 和 Go 1.27 及以上从源码构建到 `$GOPATH/bin`: ```bash -git clone https://gitea.com/gitea/gitea-mcp.git +git clone https://git.i3omb.com/gronod/gitea-mcp.git cd gitea-mcp make install ``` @@ -57,7 +57,7 @@ claude mcp add --transport stdio --scope user gitea \ "servers": { "gitea-mcp": { "command": "docker", - "args": ["run", "-i", "--rm", "-e", "GITEA_ACCESS_TOKEN", "docker.gitea.com/gitea-mcp-server"], + "args": ["run", "-i", "--rm", "-e", "GITEA_ACCESS_TOKEN", "git.i3omb.com/gronod/gitea-mcp-server"], "env": { "GITEA_ACCESS_TOKEN": "${input:gitea_token}" } @@ -93,7 +93,7 @@ claude mcp add --transport stdio --scope user gitea \ name = "gitea" transport = "stdio" command = "docker" -args = ["run", "--rm", "-i", "-e", "GITEA_ACCESS_TOKEN", "-e", "GITEA_HOST", "docker.gitea.com/gitea-mcp-server"] +args = ["run", "--rm", "-i", "-e", "GITEA_ACCESS_TOKEN", "-e", "GITEA_HOST", "git.i3omb.com/gronod/gitea-mcp-server"] [mcp_servers.env] GITEA_ACCESS_TOKEN = "TOKEN" diff --git a/README.zh-tw.md b/README.zh-tw.md index 6aa1868..e9a2187 100644 --- a/README.zh-tw.md +++ b/README.zh-tw.md @@ -4,14 +4,14 @@ **Gitea MCP 伺服器** 將 [Gitea](https://about.gitea.com) 實例接入 [Model Context Protocol](https://modelcontextprotocol.io) 客戶端,讓倉庫、問題、拉取請求等都能在相容 MCP 的聊天介面中瀏覽與管理。 -[![在 VS Code 中使用 Docker 安裝](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22docker.gitea.com/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}) [![在 VS Code Insiders 中使用 Docker 安裝](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22docker.gitea.com/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}&quality=insiders) +[![在 VS Code 中使用 Docker 安裝](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22git.i3omb.com/gronod/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}) [![在 VS Code Insiders 中使用 Docker 安裝](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=gitea&inputs=[{%22id%22:%22gitea_token%22,%22type%22:%22promptString%22,%22description%22:%22Gitea%20Personal%20Access%20Token%22,%22password%22:true}]&config={%22command%22:%22docker%22,%22args%22:[%22run%22,%22-i%22,%22--rm%22,%22-e%22,%22GITEA_ACCESS_TOKEN%22,%22git.i3omb.com/gronod/gitea-mcp-server%22],%22env%22:{%22GITEA_ACCESS_TOKEN%22:%22${input:gitea_token}%22}}&quality=insiders) ## 安裝 -可從 [發布頁面](https://gitea.com/gitea/gitea-mcp/releases) 下載二進位檔並放入 `PATH`,或使用 `docker.gitea.com/gitea-mcp-server` 映像檔,也可用 `make` 與 Go 1.27 以上從原始碼建置到 `$GOPATH/bin`: +可從 [發布頁面](https://git.i3omb.com/gronod/gitea-mcp/releases) 下載二進位檔並放入 `PATH`,或使用 `git.i3omb.com/gronod/gitea-mcp-server` 映像檔,也可用 `make` 與 Go 1.27 以上從原始碼建置到 `$GOPATH/bin`: ```bash -git clone https://gitea.com/gitea/gitea-mcp.git +git clone https://git.i3omb.com/gronod/gitea-mcp.git cd gitea-mcp make install ``` @@ -57,7 +57,7 @@ claude mcp add --transport stdio --scope user gitea \ "servers": { "gitea-mcp": { "command": "docker", - "args": ["run", "-i", "--rm", "-e", "GITEA_ACCESS_TOKEN", "docker.gitea.com/gitea-mcp-server"], + "args": ["run", "-i", "--rm", "-e", "GITEA_ACCESS_TOKEN", "git.i3omb.com/gronod/gitea-mcp-server"], "env": { "GITEA_ACCESS_TOKEN": "${input:gitea_token}" } @@ -93,7 +93,7 @@ claude mcp add --transport stdio --scope user gitea \ name = "gitea" transport = "stdio" command = "docker" -args = ["run", "--rm", "-i", "-e", "GITEA_ACCESS_TOKEN", "-e", "GITEA_HOST", "docker.gitea.com/gitea-mcp-server"] +args = ["run", "--rm", "-i", "-e", "GITEA_ACCESS_TOKEN", "-e", "GITEA_HOST", "git.i3omb.com/gronod/gitea-mcp-server"] [mcp_servers.env] GITEA_ACCESS_TOKEN = "TOKEN" diff --git a/scripts/upload-r2.sh b/scripts/upload-r2.sh deleted file mode 100755 index 300be9b..0000000 --- a/scripts/upload-r2.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/sh -# Copyright 2026 The Gitea Authors. All rights reserved. -# SPDX-License-Identifier: MIT -# -# upload-r2.sh uploads a single local file to a single object key in a -# Cloudflare R2 bucket, using curl's built-in AWS SigV4 signer (R2 is -# S3-API compatible). -# -# Usage: -# upload-r2.sh -# upload-r2.sh --check-config -# -# The second form only validates that the required environment -# variables below are set (it does not touch the network), and is -# meant to be run as an early preflight step in CI so that a missing -# R2_* secret is reported before anything is built or published. -# -# Required environment variables: -# R2_ENDPOINT Base URL of the R2 endpoint, e.g. -# https://.r2.cloudflarestorage.com -# R2_BUCKET Destination bucket name. -# R2_ACCESS_KEY_ID R2 access key id. -# R2_SECRET_ACCESS_KEY R2 secret access key. - -set -eu - -# check_env validates that all required R2_* environment variables are -# set and non-empty, so the validation logic only exists in one place -# for both the normal upload mode and --check-config. -check_env() { - missing="" - - if [ -z "${R2_ENDPOINT:-}" ]; then - missing="$missing R2_ENDPOINT" - fi - if [ -z "${R2_BUCKET:-}" ]; then - missing="$missing R2_BUCKET" - fi - if [ -z "${R2_ACCESS_KEY_ID:-}" ]; then - missing="$missing R2_ACCESS_KEY_ID" - fi - if [ -z "${R2_SECRET_ACCESS_KEY:-}" ]; then - missing="$missing R2_SECRET_ACCESS_KEY" - fi - - if [ -n "$missing" ]; then - echo "upload-r2.sh: missing required environment variable(s):$missing" >&2 - exit 1 - fi -} - -if [ "$#" -eq 1 ] && [ "$1" = "--check-config" ]; then - check_env - echo "upload-r2.sh: R2 configuration OK" - exit 0 -fi - -if [ "$#" -ne 2 ]; then - echo "usage: upload-r2.sh " >&2 - echo " upload-r2.sh --check-config" >&2 - exit 1 -fi - -local_file="$1" -remote_key="$2" - -if [ ! -f "$local_file" ]; then - echo "upload-r2.sh: local file not found: $local_file" >&2 - exit 1 -fi - -check_env - -# Strip a single trailing slash from the endpoint, if present, so that -# building the path-style URL below never produces a double slash. -endpoint="${R2_ENDPOINT%/}" -url="$endpoint/$R2_BUCKET/$remote_key" - -# Credentials are passed to curl through a config file read from -# stdin rather than as a command-line argument, so they never show up -# in `ps` output. -# -# --fail-with-body (instead of plain --fail) still exits non-zero on -# HTTP errors, but also prints R2's XML error body, which is where the -# actual error code lives (SignatureDoesNotMatch, NoSuchBucket, -# AccessDenied, ...). --retry 3 (without --retry-all-errors) only -# retries the transient cases (5xx, 408, 429, connection failures). -printf 'user = "%s:%s"\n' "$R2_ACCESS_KEY_ID" "$R2_SECRET_ACCESS_KEY" | curl \ - --config - \ - --fail-with-body \ - --silent \ - --show-error \ - --retry 3 \ - --aws-sigv4 "aws:amz:auto:s3" \ - --upload-file "$local_file" \ - "$url" -- 2.39.5