Skip to content

Commit 7195103

Browse files
committed
Chore: Add CI to release tagged image and binary
Signed-off-by: Daishan Peng <daishan@acorn.io>
1 parent 95fc784 commit 7195103

File tree

3 files changed

+385
-3
lines changed

3 files changed

+385
-3
lines changed

.github/workflows/release.yml

Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
name: Release Go Binaries
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
tag:
10+
description: 'Tag to release (e.g., v1.0.0)'
11+
required: true
12+
type: string
13+
14+
env:
15+
GO_VERSION: "1.23"
16+
17+
jobs:
18+
test:
19+
runs-on: ubuntu-latest
20+
services:
21+
postgres:
22+
image: postgres:15
23+
env:
24+
POSTGRES_DB: oauth_test
25+
POSTGRES_USER: test
26+
POSTGRES_PASSWORD: test
27+
options: >-
28+
--health-cmd pg_isready
29+
--health-interval 10s
30+
--health-timeout 5s
31+
--health-retries 5
32+
ports:
33+
- 5432:5432
34+
35+
steps:
36+
- name: Checkout repository
37+
uses: actions/checkout@v4
38+
39+
- name: Set up Go
40+
uses: actions/setup-go@v5
41+
with:
42+
go-version: ${{ env.GO_VERSION }}
43+
44+
- name: Install dependencies
45+
run: go mod download
46+
47+
- name: Run linter
48+
uses: golangci/golangci-lint-action@v4
49+
with:
50+
version: latest
51+
args: --timeout=5m
52+
53+
- name: Run tests
54+
env:
55+
TEST_DATABASE_DSN: "postgres://test:test@localhost:5432/oauth_test?sslmode=disable"
56+
run: |
57+
go test -v -short ./...
58+
go test -v -race ./...
59+
60+
build:
61+
needs: test
62+
runs-on: ubuntu-latest
63+
strategy:
64+
matrix:
65+
include:
66+
# Linux builds
67+
- goos: linux
68+
goarch: amd64
69+
name: linux-amd64
70+
- goos: linux
71+
goarch: arm64
72+
name: linux-arm64
73+
# macOS builds (universal binary will be created separately)
74+
- goos: darwin
75+
goarch: amd64
76+
name: darwin-amd64
77+
- goos: darwin
78+
goarch: arm64
79+
name: darwin-arm64
80+
81+
steps:
82+
- name: Checkout repository
83+
uses: actions/checkout@v4
84+
85+
- name: Set up Go
86+
uses: actions/setup-go@v5
87+
with:
88+
go-version: ${{ env.GO_VERSION }}
89+
90+
- name: Install dependencies
91+
run: go mod download
92+
93+
- name: Get version
94+
id: version
95+
run: |
96+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
97+
echo "VERSION=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
98+
else
99+
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
100+
fi
101+
102+
- name: Build binary
103+
env:
104+
GOOS: ${{ matrix.goos }}
105+
GOARCH: ${{ matrix.goarch }}
106+
CGO_ENABLED: 1
107+
run: |
108+
# Install cross-compilation tools for CGO (needed for SQLite)
109+
if [ "${{ matrix.goos }}" = "linux" ]; then
110+
if [ "${{ matrix.goarch }}" = "arm64" ]; then
111+
sudo apt-get update
112+
sudo apt-get install -y gcc-aarch64-linux-gnu
113+
export CC=aarch64-linux-gnu-gcc
114+
fi
115+
elif [ "${{ matrix.goos }}" = "darwin" ]; then
116+
# For macOS, we'll use a different approach with osxcross or build on macOS runners
117+
echo "Building for macOS..."
118+
fi
119+
120+
mkdir -p dist
121+
binary_name="mcp-oauth-proxy-${{ steps.version.outputs.VERSION }}-${{ matrix.name }}"
122+
if [ "${{ matrix.goos }}" = "windows" ]; then
123+
binary_name="${binary_name}.exe"
124+
fi
125+
126+
go build -o "dist/${binary_name}" \
127+
-ldflags="-X main.version=${{ steps.version.outputs.VERSION }} -X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ) -s -w" \
128+
.
129+
130+
- name: Upload binary artifact
131+
uses: actions/upload-artifact@v4
132+
with:
133+
name: binary-${{ matrix.name }}
134+
path: dist/
135+
retention-days: 1
136+
137+
build-macos:
138+
needs: test
139+
runs-on: macos-latest
140+
strategy:
141+
matrix:
142+
include:
143+
- goarch: amd64
144+
name: darwin-amd64
145+
- goarch: arm64
146+
name: darwin-arm64
147+
148+
steps:
149+
- name: Checkout repository
150+
uses: actions/checkout@v4
151+
152+
- name: Set up Go
153+
uses: actions/setup-go@v5
154+
with:
155+
go-version: ${{ env.GO_VERSION }}
156+
157+
- name: Install dependencies
158+
run: go mod download
159+
160+
- name: Get version
161+
id: version
162+
run: |
163+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
164+
echo "VERSION=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
165+
else
166+
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
167+
fi
168+
169+
- name: Build binary
170+
env:
171+
GOOS: darwin
172+
GOARCH: ${{ matrix.goarch }}
173+
CGO_ENABLED: 1
174+
run: |
175+
mkdir -p dist
176+
binary_name="mcp-oauth-proxy-${{ steps.version.outputs.VERSION }}-${{ matrix.name }}"
177+
178+
go build -o "dist/${binary_name}" \
179+
-ldflags="-X main.version=${{ steps.version.outputs.VERSION }} -X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ) -s -w" \
180+
.
181+
182+
- name: Upload binary artifact
183+
uses: actions/upload-artifact@v4
184+
with:
185+
name: binary-${{ matrix.name }}
186+
path: dist/
187+
retention-days: 1
188+
189+
create-universal-macos:
190+
needs: build-macos
191+
runs-on: macos-latest
192+
steps:
193+
- name: Get version
194+
id: version
195+
run: |
196+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
197+
echo "VERSION=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
198+
else
199+
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
200+
fi
201+
202+
- name: Download macOS binaries
203+
uses: actions/download-artifact@v4
204+
with:
205+
pattern: binary-darwin-*
206+
path: ./binaries/
207+
208+
- name: Create universal binary
209+
run: |
210+
mkdir -p dist
211+
212+
# Find the actual binary files
213+
amd64_binary=$(find ./binaries -name "*darwin-amd64*" -type f | head -1)
214+
arm64_binary=$(find ./binaries -name "*darwin-arm64*" -type f | head -1)
215+
216+
echo "AMD64 binary: $amd64_binary"
217+
echo "ARM64 binary: $arm64_binary"
218+
219+
# Create universal binary
220+
lipo -create \
221+
"$amd64_binary" \
222+
"$arm64_binary" \
223+
-output "dist/mcp-oauth-proxy-${{ steps.version.outputs.VERSION }}-darwin-universal"
224+
225+
- name: Upload universal binary artifact
226+
uses: actions/upload-artifact@v4
227+
with:
228+
name: binary-darwin-universal
229+
path: dist/
230+
retention-days: 1
231+
232+
docker:
233+
needs: test
234+
runs-on: ubuntu-latest
235+
permissions:
236+
contents: read
237+
packages: write
238+
239+
steps:
240+
- name: Checkout repository
241+
uses: actions/checkout@v4
242+
243+
- name: Get version
244+
id: version
245+
run: |
246+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
247+
echo "VERSION=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
248+
else
249+
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
250+
fi
251+
252+
- name: Set up Docker Buildx
253+
uses: docker/setup-buildx-action@v3
254+
255+
- name: Log in to GitHub Container Registry
256+
uses: docker/login-action@v3
257+
with:
258+
registry: ghcr.io
259+
username: ${{ github.actor }}
260+
password: ${{ secrets.GITHUB_TOKEN }}
261+
262+
- name: Extract metadata
263+
id: meta
264+
uses: docker/metadata-action@v5
265+
with:
266+
images: ghcr.io/${{ github.repository_owner }}/mcp-oauth-proxy
267+
tags: |
268+
type=ref,event=tag
269+
type=semver,pattern={{version}}
270+
type=semver,pattern={{major}}.{{minor}}
271+
type=semver,pattern={{major}}
272+
type=raw,value=latest,enable={{is_default_branch}}
273+
274+
- name: Build and push Docker image
275+
uses: docker/build-push-action@v5
276+
with:
277+
context: .
278+
push: true
279+
tags: ${{ steps.meta.outputs.tags }}
280+
labels: ${{ steps.meta.outputs.labels }}
281+
platforms: linux/amd64,linux/arm64
282+
build-args: |
283+
VERSION=${{ steps.version.outputs.VERSION }}
284+
BUILD_TIME=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
285+
286+
release:
287+
needs: [build, build-macos, create-universal-macos, docker]
288+
runs-on: ubuntu-latest
289+
permissions:
290+
contents: write
291+
292+
steps:
293+
- name: Checkout repository
294+
uses: actions/checkout@v4
295+
296+
- name: Get version
297+
id: version
298+
run: |
299+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
300+
echo "VERSION=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
301+
else
302+
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
303+
fi
304+
305+
- name: Download all artifacts
306+
uses: actions/download-artifact@v4
307+
with:
308+
pattern: binary-*
309+
path: ./artifacts/
310+
311+
- name: Prepare release assets
312+
run: |
313+
mkdir -p release
314+
find ./artifacts -type f -name "mcp-oauth-proxy-*" -exec cp {} release/ \;
315+
316+
# Create checksums
317+
cd release
318+
sha256sum * > checksums.txt
319+
320+
# List files for verification
321+
echo "Release files:"
322+
ls -la
323+
324+
- name: Generate changelog
325+
id: changelog
326+
run: |
327+
cat << 'EOF' > CHANGELOG.md
328+
## What's Changed in ${{ steps.version.outputs.VERSION }}
329+
330+
### 🚀 Features
331+
- Cross-platform release with native binaries for Linux and macOS
332+
- Multi-architecture Docker images for Linux (AMD64 and ARM64)
333+
334+
### 📦 Binary Downloads
335+
- **Linux AMD64**: `mcp-oauth-proxy-${{ steps.version.outputs.VERSION }}-linux-amd64`
336+
- **Linux ARM64**: `mcp-oauth-proxy-${{ steps.version.outputs.VERSION }}-linux-arm64`
337+
- **macOS Universal**: `mcp-oauth-proxy-${{ steps.version.outputs.VERSION }}-darwin-universal` (Intel + Apple Silicon)
338+
- **macOS AMD64**: `mcp-oauth-proxy-${{ steps.version.outputs.VERSION }}-darwin-amd64` (Intel)
339+
- **macOS ARM64**: `mcp-oauth-proxy-${{ steps.version.outputs.VERSION }}-darwin-arm64` (Apple Silicon)
340+
341+
### 🐳 Docker Images
342+
- **Tagged Release**: `ghcr.io/${{ github.repository_owner }}/mcp-oauth-proxy:${{ steps.version.outputs.VERSION }}`
343+
- **Latest**: `ghcr.io/${{ github.repository_owner }}/mcp-oauth-proxy:latest`
344+
- **Platforms**: linux/amd64, linux/arm64
345+
346+
### 🔍 Verification
347+
All binaries can be verified using the included `checksums.txt` file.
348+
349+
### 🛠️ Installation
350+
351+
**Binary Installation:**
352+
1. Download the appropriate binary for your platform
353+
2. Make it executable: `chmod +x mcp-oauth-proxy-*`
354+
3. Move to your PATH: `mv mcp-oauth-proxy-* /usr/local/bin/mcp-oauth-proxy`
355+
356+
**Docker Installation:**
357+
```bash
358+
docker pull ghcr.io/${{ github.repository_owner }}/mcp-oauth-proxy:${{ steps.version.outputs.VERSION }}
359+
# or
360+
docker pull ghcr.io/${{ github.repository_owner }}/mcp-oauth-proxy:latest
361+
```
362+
363+
**Full Changelog**: https://github.com/${{ github.repository }}/commits/${{ steps.version.outputs.VERSION }}
364+
EOF
365+
366+
- name: Create Release
367+
uses: ncipollo/release-action@v1
368+
with:
369+
tag: ${{ steps.version.outputs.VERSION }}
370+
name: Release ${{ steps.version.outputs.VERSION }}
371+
bodyFile: CHANGELOG.md
372+
artifacts: "release/*"
373+
draft: false
374+
prerelease: ${{ contains(steps.version.outputs.VERSION, '-') }}
375+
token: ${{ secrets.GITHUB_TOKEN }}
376+
generateReleaseNotes: true

Dockerfile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,14 @@ RUN go mod tidy
1515
# Copy source code
1616
COPY . .
1717

18-
# Build the application
19-
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o oauth-proxy main.go
18+
# Accept build arguments
19+
ARG VERSION=dev
20+
ARG BUILD_TIME=unknown
21+
22+
# Build the application with version info
23+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo \
24+
-ldflags="-X main.version=${VERSION} -X main.buildTime=${BUILD_TIME} -s -w" \
25+
-o oauth-proxy .
2026

2127
# Final stage
2228
FROM alpine:latest

0 commit comments

Comments
 (0)