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
6 changes: 5 additions & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
* @basecamp/cli
* @basecamp/sip
actions/ @basecamp/sip
seed/ @basecamp/sip
.github/workflows/ @basecamp/sip
scripts/ @basecamp/sip
6 changes: 6 additions & 0 deletions .github/codeql/codeql-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
queries:
- uses: security-and-quality

query-filters:
- exclude:
kind: [diagnostic, metric]
26 changes: 23 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
# Dependabot configuration
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates

version: 2

updates:
# Go modules
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
day: monday
time: "06:00"
timezone: America/Chicago
open-pull-requests-limit: 10
groups:
go-dependencies:
patterns:
- "*"
open-pull-requests-limit: 5
cooldown:
default-days: 2
semver-major-days: 7
semver-minor-days: 3
semver-patch-days: 2
commit-message:
prefix: "deps"

# GitHub Actions
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
time: "06:00"
timezone: America/Chicago
open-pull-requests-limit: 10
groups:
actions:
github-actions:
patterns:
- "*"
open-pull-requests-limit: 5
cooldown:
default-days: 2
commit-message:
prefix: "ci"
4 changes: 3 additions & 1 deletion .github/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
changelog:
exclude:
labels:
- dependencies
- github-actions
authors:
- dependabot
- dependabot[bot]
categories:
- title: Breaking Changes
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Dependabot auto-merge

on: pull_request

permissions:
contents: write
pull-requests: write

jobs:
auto-merge:
name: Auto-merge
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2
id: metadata
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Auto-merge minor and patch updates
if: steps.metadata.outputs.update-type != 'version-update:semver-major' && steps.metadata.outputs.package-ecosystem != 'github_actions'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
136 changes: 136 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
name: Release

on:
push:
tags:
- 'v*'

permissions:
contents: write
security-events: write
pull-requests: read

jobs:
security:
name: Security
uses: ./.github/workflows/security.yml
secrets: inherit

test:
name: Test gate
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0

- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version-file: go.mod

- name: Install golangci-lint
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9
with:
version: v2.9.0
install-only: true

- name: Check formatting
run: test -z "$(gofmt -l .)" || (echo "Run 'gofmt -w .' to fix formatting" && gofmt -l . && exit 1)

- name: Vet
run: go vet ./...

- name: Lint
run: golangci-lint run

- name: Check go.mod tidiness
run: |
go mod tidy
git diff --exit-code go.mod go.sum

- name: Test
run: go test -v ./...

- name: Test (race detector)
run: go test -race -count=1 ./...

- name: Govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...

- name: Verify tag is on main
run: |
git fetch origin main
if ! git merge-base --is-ancestor "${{ github.sha }}" origin/main; then
echo "Error: tag is not on the main branch"
exit 1
fi

publish:
name: Publish
runs-on: ubuntu-latest
needs: [test, security]
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0

- name: Verify tag is on main
run: |
git fetch origin main
if ! git merge-base --is-ancestor "${{ github.sha }}" origin/main; then
echo "Error: tag is not on the main branch"
exit 1
fi

- name: Confirm module availability
run: echo "Tagged Go module ${{ github.ref_name }} published via module proxy"

sync-skills:
name: Sync skills
runs-on: ubuntu-latest
needs: [publish]
if: vars.SKILLS_APP_ID != ''
continue-on-error: true
timeout-minutes: 5
concurrency:
group: sync-skills
cancel-in-progress: false
permissions:
contents: read
env:
HAS_SKILLS_KEY: ${{ secrets.SKILLS_APP_PRIVATE_KEY && 'true' || '' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Check prerequisites
id: check
run: |
if [ -z "$HAS_SKILLS_KEY" ]; then
echo "::warning::SKILLS_APP_PRIVATE_KEY secret is not set — skipping skills sync"
echo "ready=false" >> "$GITHUB_OUTPUT"
elif ! ls skills/*/SKILL.md >/dev/null 2>&1; then
echo "No skill files found — skipping sync"
echo "ready=false" >> "$GITHUB_OUTPUT"
else
echo "ready=true" >> "$GITHUB_OUTPUT"
fi

- name: Generate token
if: steps.check.outputs.ready == 'true'
id: skills-token
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
with:
app-id: ${{ vars.SKILLS_APP_ID }}
private-key: ${{ secrets.SKILLS_APP_PRIVATE_KEY }}
owner: basecamp
repositories: skills

- name: Sync skills
if: steps.check.outputs.ready == 'true'
run: CLI_NAME=cli SKILLS_TOKEN=${{ steps.skills-token.outputs.token }} RELEASE_TAG=${{ github.ref_name }} SOURCE_SHA=${{ github.sha }} scripts/sync-skills.sh
139 changes: 139 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Security

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * 1'
workflow_call:
workflow_dispatch:

permissions:
contents: read
security-events: write
pull-requests: read

jobs:
secrets:
name: Secret scanning
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0

- name: Install gitleaks
run: |
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz | tar -xz
sudo mv gitleaks /usr/local/bin/

- name: Run gitleaks
run: make secrets

trivy:
name: Trivy vulnerability scan
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Run Trivy vulnerability scanner (filesystem)
uses: aquasecurity/trivy-action@e368e328979b113139d6f9068e03accaed98a518 # 0.34.1
with:
scan-type: 'fs'
scan-ref: '.'
severity: 'HIGH,CRITICAL'
exit-code: '1'
ignore-unfixed: true
format: 'sarif'
output: 'trivy-results.sarif'
version: 'v0.69.3'

- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4
if: always()
continue-on-error: true # Requires GitHub Advanced Security
with:
sarif_file: 'trivy-results.sarif'

gosec:
name: Gosec
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version-file: go.mod

- name: Install gosec
run: go install github.com/securego/gosec/v2/cmd/gosec@v2.23.0

- name: Run gosec
run: gosec -no-fail -fmt sarif -out gosec-results.sarif ./...

- name: Upload gosec scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4
if: always()
continue-on-error: true # Requires GitHub Advanced Security
with:
sarif_file: 'gosec-results.sarif'

dependency-review:
name: Dependency review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4
continue-on-error: true # Requires GitHub Advanced Security

codeql:
name: CodeQL
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
with:
go-version-file: go.mod

- name: Initialize CodeQL
uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4
with:
languages: go
build-mode: manual
config-file: ./.github/codeql/codeql-config.yml

- name: Build
run: go build ./...

- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4
with:
category: codeql-go
upload: never
output: sarif-results

- name: Upload SARIF to GitHub Security tab
uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4
continue-on-error: true # Requires GitHub Advanced Security
with:
sarif_file: sarif-results
category: codeql-go
Loading
Loading