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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
# Stage 1: Validation
validate:
name: Validation
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -30,7 +30,7 @@ jobs:
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: server/go.sum
cache: false

- name: Verify Go version
run: |
Expand Down Expand Up @@ -61,8 +61,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: web/package-lock.json

- name: Run npm audit
working-directory: web
Expand All @@ -72,7 +70,7 @@ jobs:
# Stage 2: Test Matrix
test-server:
name: Server Tests
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: validate
steps:
- name: Checkout code
Expand All @@ -82,10 +80,13 @@ jobs:
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: server/go.sum
cache: false

- name: Run server tests
working-directory: server
env:
KUBERNETES_SERVICE_HOST: ''
KUBERNETES_SERVICE_PORT: ''
run: |
go test -v -race -coverprofile=coverage.out -covermode=atomic ./...

Expand All @@ -106,10 +107,11 @@ jobs:
with:
name: server-coverage
path: server/coverage.out
retention-days: 3

test-web:
name: Web Tests
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: validate
steps:
- name: Checkout code
Expand All @@ -119,8 +121,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: web/package-lock.json

- name: Install dependencies
working-directory: web
Expand All @@ -137,7 +137,7 @@ jobs:
# Stage 3: Build Docker Image
build:
name: Build Docker Image
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: [test-server, test-web]
permissions:
contents: read
Expand Down Expand Up @@ -201,8 +201,6 @@ jobs:
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
GO_VERSION=1.24.3
NODE_MAJOR=20
Expand All @@ -217,7 +215,7 @@ jobs:
# resolution happens before `if` is evaluated. An in-line action has no such problem.
grype-scan:
name: Security Scan (Grype)
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: [build]
if: false # Disabled - KubeTTY image too large for Grype to scan in CI (same reasoning as the prior Trivy job)
permissions:
Expand All @@ -236,7 +234,7 @@ jobs:
# Stage 5: Helm Chart Validation
helm-validate:
name: Helm Chart Validation
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: validate
steps:
- name: Checkout code
Expand Down Expand Up @@ -278,11 +276,12 @@ jobs:
with:
name: helm-charts
path: helm-packages/*.tgz
retention-days: 3

# Stage 6: Deploy to Production
deploy-production:
name: Deploy to Production
runs-on: self-hosted-linux
runs-on: self-hosted-linux-dfw
needs: [build, helm-validate]
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
permissions:
Expand Down
97 changes: 53 additions & 44 deletions .github/workflows/validate-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,23 @@ on:
workflow_dispatch:

env:
GO_VERSION: '1.23'
NODE_VERSION: '20'
COVERAGE_THRESHOLD: 60
COVERAGE_THRESHOLD: 35

jobs:
# Fast validation checks
validate:
name: Quick Validation
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep untrusted PR jobs off self-hosted runners

When a pull request contains untrusted code, this workflow now checks it out and executes Go, npm, and database test commands on the DFW self-hosted infrastructure; the same regression exists in the pull_request path of pipeline.yml. A contributor can modify tests or package.json scripts to inspect the runner pod's credentials or reach services on its internal network, and ephemeral ARC runners only limit persistence after the job. Keep PR execution on GitHub-hosted runners or place it on a separately isolated runner group with no sensitive credentials or internal network access.

Useful? React with 👍 / 👎.

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: server/go.sum

- name: Verify Go version
run: |
go version
go version | grep -q "go1.23" || (echo "ERROR: Must use Go 1.23.x" && exit 1)
go-version-file: server/go.mod
cache: false

- name: Check Go formatting
working-directory: server
Expand Down Expand Up @@ -68,8 +62,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: web/package-lock.json

- name: Check for package-lock changes
working-directory: web
Expand All @@ -85,7 +77,7 @@ jobs:
# Server tests with coverage (unit tests only)
test-server:
name: Server Tests
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: validate
steps:
- name: Checkout code
Expand All @@ -94,19 +86,32 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: server/go.sum
go-version-file: server/go.mod
cache: false

- name: Run server tests
working-directory: server
env:
KUBERNETES_SERVICE_HOST: ''
KUBERNETES_SERVICE_PORT: ''
run: |
go test -v -race -coverprofile=coverage.out -covermode=atomic ./...

- name: Upload server coverage report
uses: actions/upload-artifact@v4
with:
name: server-coverage
path: server/coverage.out
retention-days: 3

# Integration tests with PostgreSQL
test-integration:
name: Integration Tests
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: validate
permissions:
contents: read
issues: write
services:
postgres:
image: postgres:16-alpine
Expand All @@ -128,12 +133,20 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: server/go.sum
go-version-file: server/go.mod
cache: false

- name: Initialize test database
run: |
PGPASSWORD=kubetty_test psql -h localhost -U kubetty_test -d kubetty_test -f server/scripts/init-test-db.sql
for migration in server/migrations/*.up.sql; do
echo "Applying ${migration}"
PGPASSWORD=kubetty_test psql \
-v ON_ERROR_STOP=1 \
-h localhost \
-U kubetty_test \
-d kubetty_test \
-f "${migration}"
done

- name: Run integration tests
working-directory: server
Expand All @@ -144,6 +157,8 @@ jobs:
CNPG_PASSWORD: kubetty_test
CNPG_DATABASE: kubetty_test
KUBETTY_TEST_DB: "true"
KUBERNETES_SERVICE_HOST: ''
KUBERNETES_SERVICE_PORT: ''
run: |
go test -v -race -run Integration ./...

Expand All @@ -156,6 +171,8 @@ jobs:
CNPG_PASSWORD: kubetty_test
CNPG_DATABASE: kubetty_test
KUBETTY_TEST_DB: "true"
KUBERNETES_SERVICE_HOST: ''
KUBERNETES_SERVICE_PORT: ''
run: |
go test -v -race -coverprofile=coverage-integration.out -covermode=atomic ./...

Expand All @@ -164,12 +181,14 @@ jobs:
with:
name: integration-coverage
path: server/coverage-integration.out
retention-days: 7
retention-days: 3

- name: Check test coverage
id: coverage
working-directory: server
run: |
coverage=$(go tool cover -func=coverage.out | grep total | awk '{print substr($3, 1, length($3)-1)}')
coverage=$(go tool cover -func=coverage-integration.out | grep total | awk '{print substr($3, 1, length($3)-1)}')
echo "percent=${coverage}" >> "$GITHUB_OUTPUT"
echo "### Test Coverage: ${coverage}%" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

Expand All @@ -180,25 +199,13 @@ jobs:
fi
echo "✅ Coverage ${coverage}% meets threshold ${threshold}%" >> $GITHUB_STEP_SUMMARY

- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: server-coverage
path: server/coverage.out
retention-days: 7

- name: Comment coverage on PR
if: github.event_name == 'pull_request'
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const coverage = fs.readFileSync('server/coverage.out', 'utf8');
const lines = coverage.trim().split('\n');
const totalLine = lines[lines.length - 1];
const match = totalLine.match(/(\d+\.\d+)%/);
const coveragePercent = match ? match[1] : 'unknown';

const coveragePercent = ${{ toJSON(steps.coverage.outputs.percent) }};
const threshold = process.env.COVERAGE_THRESHOLD;
const emoji = parseFloat(coveragePercent) >= parseFloat(threshold) ? '✅' : '❌';

Expand All @@ -207,7 +214,7 @@ jobs:
`- **Threshold**: ${threshold}%\n` +
`- **Status**: ${parseFloat(coveragePercent) >= parseFloat(threshold) ? 'PASS' : 'FAIL'}`;

github.rest.issues.createComment({
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
Expand All @@ -217,7 +224,7 @@ jobs:
# Web tests
test-web:
name: Web Tests
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: validate
steps:
- name: Checkout code
Expand All @@ -227,8 +234,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: web/package-lock.json

- name: Install dependencies
working-directory: web
Expand All @@ -248,15 +253,15 @@ jobs:
echo "### Frontend Build Size" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
du -sh dist/ >> $GITHUB_STEP_SUMMARY
du -sh ../server/cmd/gateway/ui/dist/ >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
ls -lh dist/assets/*.js dist/assets/*.css 2>/dev/null | awk '{print $5, $9}' >> $GITHUB_STEP_SUMMARY || echo "No asset files found" >> $GITHUB_STEP_SUMMARY
ls -lh ../server/cmd/gateway/ui/dist/assets/*.js ../server/cmd/gateway/ui/dist/assets/*.css 2>/dev/null | awk '{print $5, $9}' >> $GITHUB_STEP_SUMMARY || echo "No asset files found" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY

# Helm chart validation
helm-validate:
name: Helm Validation
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: validate
steps:
- name: Checkout code
Expand Down Expand Up @@ -323,9 +328,12 @@ jobs:
# Summary job that all others depend on
pr-validation-summary:
name: PR Validation Summary
runs-on: ubuntu-latest
runs-on: self-hosted-linux-dfw
needs: [validate, test-server, test-integration, test-web, helm-validate]
if: always()
permissions:
contents: read
issues: write
steps:
- name: Check validation results
run: |
Expand Down Expand Up @@ -354,10 +362,11 @@ jobs:

- name: Update PR status
if: github.event_name == 'pull_request' && needs.validate.result == 'success' && needs.test-server.result == 'success' && needs.test-integration.result == 'success' && needs.test-web.result == 'success' && needs.helm-validate.result == 'success'
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
Expand Down
Loading