Skip to content

Commit d620a43

Browse files
committed
feat: add docker publishing with environment variable conventions
- Add GitHub Actions workflow for Docker publishing - Support GitHub Container Registry and private registries - Implement environment variable naming convention (NODE_, NATS_, LOG_, etc.) - Add dynamic port configuration via NODE_PORT - Simplify documentation and remove noise - Support multi-platform builds and security scanning
1 parent 59ef34e commit d620a43

File tree

6 files changed

+389
-90
lines changed

6 files changed

+389
-90
lines changed

.env.example

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,37 @@
1-
# Droq Node Template Environment Variables
2-
# Copy this file to .env and update with your values
1+
# Environment Variable Naming Convention:
2+
# NODE_ - Node configuration
3+
# NATS_ - NATS configuration
4+
# LOG_ - Logging configuration
5+
# DB_ - Database configuration
6+
# HTTP_ - HTTP client configuration
7+
# SERVICE_ - Service-specific configuration
8+
# METRICS_ - Metrics and monitoring
39

410
# Node Configuration
511
NODE_NAME=droq-node-template
12+
NODE_PORT=8000
13+
NODE_EXTERNAL_PORT=8000
14+
NODE_HEALTH_CHECK_ENABLED=true
15+
16+
# Logging
617
LOG_LEVEL=INFO
718

819
# NATS Configuration
920
NATS_URL=nats://localhost:4222
21+
NATS_CLIENT_NAME=${NODE_NAME:-droq-node-template}
1022
STREAM_NAME=droq-stream
1123

12-
# HTTP Client Configuration (optional)
13-
# BASE_URL=https://api.example.com
24+
# Optional: NATS Authentication
25+
# NATS_USERNAME=nats-user
26+
# NATS_PASSWORD=nats-pass
27+
28+
# Optional: HTTP Configuration
29+
# HTTP_BASE_URL=https://api.example.com
30+
# HTTP_API_KEY=your-api-key
31+
32+
# Optional: Database Configuration
33+
# DB_URL=postgresql://user:pass@localhost/dbname
1434

15-
# Add your custom environment variables below
16-
# API_KEY=your-api-key-here
17-
# DATABASE_URL=postgresql://user:pass@localhost/dbname
35+
# Optional: Metrics Configuration
36+
# METRICS_PORT=8081
37+
# METRICS_ENABLED=true
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
name: Docker Build and Publish
2+
3+
on:
4+
push:
5+
branches: [main]
6+
tags: ['v*']
7+
pull_request:
8+
branches: [main]
9+
workflow_dispatch:
10+
inputs:
11+
publish:
12+
description: 'Publish to registry'
13+
required: false
14+
default: 'false'
15+
type: boolean
16+
17+
env:
18+
REGISTRY: ghcr.io
19+
IMAGE_NAME: ${{ github.repository }}
20+
21+
jobs:
22+
build-and-test:
23+
runs-on: ubuntu-latest
24+
outputs:
25+
image-digest: ${{ steps.build.outputs.digest }}
26+
image-tag: ${{ steps.meta.outputs.tags }}
27+
should-publish: ${{ steps.should-publish.outputs.result }}
28+
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
33+
- name: Set up Docker Buildx
34+
uses: docker/setup-buildx-action@v3
35+
36+
- name: Extract metadata
37+
id: meta
38+
uses: docker/metadata-action@v5
39+
with:
40+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
41+
tags: |
42+
type=ref,event=branch
43+
type=ref,event=pr
44+
type=semver,pattern={{version}}
45+
type=semver,pattern={{major}}.{{minor}}
46+
type=semver,pattern={{major}}
47+
type=raw,value=latest,enable={{is_default_branch}}
48+
49+
- name: Determine if should publish
50+
id: should-publish
51+
run: |
52+
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]] || \
53+
[[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == refs/tags/* ]] || \
54+
[[ "${{ github.event.inputs.publish }}" == "true" ]]; then
55+
echo "result=true" >> $GITHUB_OUTPUT
56+
else
57+
echo "result=false" >> $GITHUB_OUTPUT
58+
fi
59+
60+
- name: Build Docker image
61+
id: build
62+
uses: docker/build-push-action@v5
63+
with:
64+
context: .
65+
platforms: linux/amd64,linux/arm64
66+
push: false
67+
load: false
68+
tags: ${{ steps.meta.outputs.tags }}
69+
labels: ${{ steps.meta.outputs.labels }}
70+
cache-from: type=gha
71+
cache-to: type=gha,mode=max
72+
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push=false
73+
74+
publish:
75+
runs-on: ubuntu-latest
76+
needs: build-and-test
77+
if: needs.build-and-test.outputs.should-publish == 'true'
78+
environment: production
79+
80+
steps:
81+
- name: Checkout repository
82+
uses: actions/checkout@v4
83+
84+
- name: Set up Docker Buildx
85+
uses: docker/setup-buildx-action@v3
86+
87+
- name: Log in to Container Registry
88+
uses: docker/login-action@v3
89+
with:
90+
registry: ${{ env.REGISTRY }}
91+
username: ${{ github.actor }}
92+
password: ${{ secrets.GITHUB_TOKEN }}
93+
94+
- name: Extract metadata
95+
id: meta
96+
uses: docker/metadata-action@v5
97+
with:
98+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
99+
tags: |
100+
type=ref,event=branch
101+
type=semver,pattern={{version}}
102+
type=semver,pattern={{major}}.{{minor}}
103+
type=semver,pattern={{major}}
104+
type=raw,value=latest,enable={{is_default_branch}}
105+
106+
- name: Build and push Docker image
107+
uses: docker/build-push-action@v5
108+
with:
109+
context: .
110+
platforms: linux/amd64,linux/arm64
111+
push: true
112+
tags: ${{ steps.meta.outputs.tags }}
113+
labels: ${{ steps.meta.outputs.labels }}
114+
cache-from: type=gha
115+
cache-to: type=gha,mode=max
116+
117+
- name: Generate SBOM
118+
uses: anchore/sbom-action@v0
119+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
120+
with:
121+
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
122+
format: spdx-json
123+
output-file: sbom.spdx.json
124+
125+
- name: Upload SBOM
126+
uses: actions/upload-artifact@v4
127+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
128+
with:
129+
name: sbom
130+
path: sbom.spdx.json
131+
132+
publish-private-registry:
133+
runs-on: ubuntu-latest
134+
needs: build-and-test
135+
if: needs.build-and-test.outputs.should-publish == 'true' && secrets.PRIVATE_REGISTRY_URL != ''
136+
environment: production
137+
138+
steps:
139+
- name: Checkout repository
140+
uses: actions/checkout@v4
141+
142+
- name: Set up Docker Buildx
143+
uses: docker/setup-buildx-action@v3
144+
145+
- name: Log in to Private Registry
146+
uses: docker/login-action@v3
147+
with:
148+
registry: ${{ secrets.PRIVATE_REGISTRY_URL }}
149+
username: ${{ secrets.PRIVATE_REGISTRY_USERNAME }}
150+
password: ${{ secrets.PRIVATE_REGISTRY_PASSWORD }}
151+
152+
- name: Extract metadata for private registry
153+
id: meta-private
154+
uses: docker/metadata-action@v5
155+
with:
156+
images: ${{ secrets.PRIVATE_REGISTRY_URL }}/${{ secrets.PRIVATE_REGISTRY_IMAGE_NAME || github.repository }}
157+
tags: |
158+
type=ref,event=branch
159+
type=semver,pattern={{version}}
160+
type=semver,pattern={{major}}.{{minor}}
161+
type=semver,pattern={{major}}
162+
type=raw,value=latest,enable={{is_default_branch}}
163+
164+
- name: Build and push to Private Registry
165+
uses: docker/build-push-action@v5
166+
with:
167+
context: .
168+
platforms: linux/amd64,linux/arm64
169+
push: true
170+
tags: ${{ steps.meta-private.outputs.tags }}
171+
labels: ${{ steps.meta-private.outputs.labels }}
172+
cache-from: type=gha
173+
cache-to: type=gha,mode=max
174+
175+
security-scan:
176+
runs-on: ubuntu-latest
177+
needs: [build-and-test, publish]
178+
if: needs.build-and-test.outputs.should-publish == 'true'
179+
environment: production
180+
181+
steps:
182+
- name: Run Trivy vulnerability scanner
183+
uses: aquasecurity/trivy-action@master
184+
with:
185+
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build-and-test.outputs.image-digest }}
186+
format: 'sarif'
187+
output: 'trivy-results.sarif'
188+
189+
- name: Upload Trivy scan results to GitHub Security tab
190+
uses: github/codeql-action/upload-sarif@v3
191+
if: always()
192+
with:
193+
sarif_file: 'trivy-results.sarif'

Dockerfile

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,29 @@
1-
# Dockerfile template for Droq nodes
2-
# This is an agnostic template - customize as needed for your node
3-
41
FROM python:3.11-slim
52

6-
# Set working directory
73
WORKDIR /app
84

9-
# Install system dependencies
10-
# Uncomment and add system packages as needed:
11-
# RUN apt-get update && apt-get install -y \
12-
# gcc \
13-
# g++ \
14-
# make \
15-
# curl \
16-
# && rm -rf /var/lib/apt/lists/*
17-
18-
# Install uv
195
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
206

21-
# Copy dependency files
227
COPY pyproject.toml uv.lock* ./
238

24-
# Install dependencies using uv
25-
# Install dependencies directly (not as editable package)
269
RUN uv pip install --system nats-py aiohttp || \
2710
(uv pip compile pyproject.toml -o requirements.txt && \
2811
uv pip install --system -r requirements.txt)
2912

30-
# Copy source code
3113
COPY src/ ./src/
3214

33-
# Create non-root user for security
3415
RUN useradd -m -u 1000 nodeuser && chown -R nodeuser:nodeuser /app
3516
USER nodeuser
3617

37-
# Set environment variables
3818
ENV PYTHONPATH=/app
3919
ENV PYTHONUNBUFFERED=1
20+
ENV NODE_PORT=8000
21+
22+
EXPOSE ${NODE_PORT}
4023

41-
# Optional: Health check
42-
# Uncomment and customize as needed:
43-
# HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
44-
# CMD python -c "import sys; sys.exit(0)"
24+
ARG NODE_HEALTH_CHECK_ENABLED=false
25+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
26+
CMD python -c "import sys; sys.exit(0)" || exit 1
4527

46-
# Run the node
47-
# Update this command to match your entry point
48-
CMD ["uv", "run", "python", "-m", "node.main"]
28+
CMD ["sh", "-c", "exec uv run python -m node.main --port=${NODE_PORT:-8000}"]
4929

0 commit comments

Comments
 (0)