Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
35 changes: 19 additions & 16 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
# Optional service-scoped public egress proxies. Git and the IM SDKs use the
# backend proxy; tinyauth uses its own proxy for GitHub OAuth token exchange.
# Other Compose services remain proxy-free.
MOONCODE_BACKEND_HTTP_PROXY=http://proxy.example:8123
MOONCODE_BACKEND_HTTPS_PROXY=http://proxy.example:8123
MOONCODE_TINYAUTH_HTTP_PROXY=http://proxy.example:8123
MOONCODE_TINYAUTH_HTTPS_PROXY=http://proxy.example:8123
# MoonCode application secrets. Generate distinct values with:
# openssl rand -base64 32
MOONCODE_SECRETS_KEY=
MOONCODE_AUTH_CSRF_KEY=
# Direct peer CIDRs allowed to provide Tinyauth Remote-* headers.
MOONCODE_AUTH_TRUSTED_PROXY_CIDRS=172.30.0.2/32

# Optional development overrides. Production deployments must generate and
# provide distinct 32-byte base64 values rather than using Compose defaults.
MOONCODE_SECRET_STORE_MASTER_KEY=
MOONCODE_CSRF_KEY=
# Maximum persistent size of one managed bare Git mirror (default: 10 GiB).
MOONCODE_REPOSITORY_MAX_MIRROR_BYTES=10737418240
MOONCODE_REPOSITORY_MAX_PER_WORKSPACE=100
MOONCODE_ANALYSIS_MAX_CONCURRENT_PER_WORKSPACE=10
MOONCODE_METRICS_WORKER_ADDRESS=:9090

# Required by the tinyauth GitHub OAuth provider. Create a GitHub OAuth App
# whose callback URL is:
# http://auth.mooncode.localhost:3100/api/oauth/callback/github
# A MoonCode-scoped token created by the self-hosted Hatchet instance.
HATCHET_CLIENT_TOKEN=

# GitHub OAuth App used only by Tinyauth for MoonCode SaaS login.
# Callback: http://mooncode.localhost:3100/api/oauth/callback/github
GITHUB_OAUTH_CLIENT_ID=
GITHUB_OAUTH_CLIENT_SECRET_FILE=./secrets/github_oauth_client_secret

# invite_only is recommended for private testing; use public for SaaS signup.
MOONCODE_REGISTRATION_MODE=invite_only
# Optional service-scoped outbound proxies.
MOONCODE_BACKEND_HTTP_PROXY=
MOONCODE_TINYAUTH_HTTP_PROXY=
102 changes: 99 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ jobs:
git diff --exit-code

- name: Lint Go
uses: golangci/golangci-lint-action@v8
uses: golangci/golangci-lint-action@v9.3.0
with:
version: v2.5.0
args: ./cmd/... ./internal/... ./pkg/... ./migrations/...
version: v2.12.2
args: ./api/... ./cmd/... ./internal/... ./pkg/... ./migrations/...

- name: Test and build
run: make test vet frontend-i18n frontend-lint frontend-build
Expand All @@ -71,7 +71,25 @@ jobs:
go-version-file: go.mod
cache: true

- name: Start PostgreSQL
run: >-
docker run --rm -d --name mooncode-ci-postgres
-e POSTGRES_USER=mooncode
-e POSTGRES_PASSWORD=mooncode
-e POSTGRES_DB=mooncode
-p 5432:5432 postgres:17-bookworm

- name: Wait for PostgreSQL
run: |
for attempt in $(seq 1 30); do
docker exec mooncode-ci-postgres pg_isready -U mooncode -d mooncode && exit 0
sleep 1
done
exit 1

- name: Run integration tests
env:
MOONCODE_TEST_DATABASE_URL: postgres://mooncode:mooncode@127.0.0.1:5432/mooncode?sslmode=disable
run: make integration-test

secrets:
Expand All @@ -90,3 +108,81 @@ jobs:
-v "$PWD:/repo:ro"
zricethezav/gitleaks:v8.28.0
detect --source=/repo --redact --verbose --exit-code=1

dependency-review:
name: Dependency review
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Review dependency changes
uses: actions/dependency-review-action@v4
with:
fail-on-severity: high

supply-chain:
name: Vulnerabilities, images, and SBOM
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true

- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
cache-dependency-path: frontend/package-lock.json

- name: Install frontend dependencies
run: npm ci --prefix frontend

- name: Scan Go dependencies
run: >-
go run golang.org/x/vuln/cmd/govulncheck@v1.1.4
./api/... ./cmd/... ./internal/... ./pkg/... ./migrations/...

- name: Audit frontend dependencies
run: npm --prefix frontend audit --audit-level=high

- name: Build deployment images
run: |
docker build --file deploy/backend.Dockerfile --tag mooncode-backend:ci .
docker build --file deploy/frontend.Dockerfile --tag mooncode-frontend:ci .

- name: Scan backend image
run: >-
docker run --rm
-v /var/run/docker.sock:/var/run/docker.sock
aquasec/trivy:0.66.0
image --exit-code 1 --ignore-unfixed --severity HIGH,CRITICAL mooncode-backend:ci

- name: Scan frontend image
run: >-
docker run --rm
-v /var/run/docker.sock:/var/run/docker.sock
aquasec/trivy:0.66.0
image --exit-code 1 --ignore-unfixed --severity HIGH,CRITICAL mooncode-frontend:ci

- name: Generate image SBOMs
run: |
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock anchore/syft:v1.33.0 mooncode-backend:ci -o spdx-json > mooncode-backend.spdx.json
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock anchore/syft:v1.33.0 mooncode-frontend:ci -o spdx-json > mooncode-frontend.spdx.json

- name: Upload SBOMs
uses: actions/upload-artifact@v4
with:
name: mooncode-image-sboms
path: mooncode-*.spdx.json
if-no-files-found: error
retention-days: 30
196 changes: 196 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
name: Release

on:
push:
tags: ["v*"]

permissions:
contents: read

jobs:
verify:
name: Verify release source
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Validate semantic version tag
shell: bash
run: |
if [[ ! "${GITHUB_REF_NAME}" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "Release tag must be a semantic version such as v1.2.3 or v1.2.3-rc.1" >&2
exit 1
fi
version_without_build="${GITHUB_REF_NAME%%+*}"
if [[ "${version_without_build}" == *-* ]]; then
prerelease="${version_without_build#*-}"
IFS='.' read -ra identifiers <<< "${prerelease}"
for identifier in "${identifiers[@]}"; do
if [[ "${identifier}" =~ ^[0-9]+$ && "${identifier}" == 0* && "${identifier}" != "0" ]]; then
echo "Numeric prerelease identifiers must not contain leading zeroes" >&2
exit 1
fi
done
fi

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true

- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
cache-dependency-path: frontend/package-lock.json

- name: Install frontend dependencies
run: npm ci --prefix frontend

- name: Verify generated and module files
run: |
go mod tidy
make generate/sqlc
git diff --exit-code

- name: Lint Go
uses: golangci/golangci-lint-action@v9.3.0
with:
version: v2.12.2
args: ./api/... ./cmd/... ./internal/... ./pkg/... ./migrations/...

- name: Run release checks
run: make test vet frontend-i18n frontend-lint frontend-build

- name: Start PostgreSQL
run: >-
docker run --rm -d --name mooncode-release-postgres
-e POSTGRES_USER=mooncode
-e POSTGRES_PASSWORD=mooncode
-e POSTGRES_DB=mooncode
-p 5432:5432 postgres:17-bookworm

- name: Wait for PostgreSQL
run: |
for attempt in $(seq 1 30); do
docker exec mooncode-release-postgres pg_isready -U mooncode -d mooncode && exit 0
sleep 1
done
exit 1

- name: Run integration tests
env:
MOONCODE_TEST_DATABASE_URL: postgres://mooncode:mooncode@127.0.0.1:5432/mooncode?sslmode=disable
run: make integration-test

images:
name: Publish ${{ matrix.component }} image
needs: verify
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read
packages: write
id-token: write
attestations: write
strategy:
fail-fast: false
matrix:
include:
- component: backend
dockerfile: deploy/backend.Dockerfile
- component: frontend
dockerfile: deploy/frontend.Dockerfile
steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Set build time
id: build-time
run: echo "value=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Compute image name
id: image
shell: bash
run: |
echo "name=ghcr.io/${GITHUB_REPOSITORY,,}/${{ matrix.component }}" >> "${GITHUB_OUTPUT}"
version_without_build="${GITHUB_REF_NAME%%+*}"
if [[ "${version_without_build}" == *-* ]]; then
echo "latest=false" >> "${GITHUB_OUTPUT}"
else
echo "latest=true" >> "${GITHUB_OUTPUT}"
fi

- name: Compute image metadata
id: metadata
uses: docker/metadata-action@v5
with:
images: ${{ steps.image.outputs.name }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable=${{ steps.image.outputs.latest }}

- name: Build and publish image
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
build-args: |
VERSION=${{ github.ref_name }}
COMMIT=${{ github.sha }}
BUILD_TIME=${{ steps.build-time.outputs.value }}
cache-from: type=gha,scope=${{ matrix.component }}
cache-to: type=gha,mode=max,scope=${{ matrix.component }}
provenance: mode=max
sbom: true

- name: Install Cosign
uses: sigstore/cosign-installer@v3.9.2

- name: Sign published image
env:
DIGEST: ${{ steps.build.outputs.digest }}
IMAGE: ${{ steps.image.outputs.name }}
run: cosign sign --yes "${IMAGE}@${DIGEST}"

- name: Attest image provenance
uses: actions/attest-build-provenance@v3
with:
subject-name: ${{ steps.image.outputs.name }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true

github-release:
name: Publish GitHub release
needs: images
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Create release notes
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
Loading
Loading