Skip to content

chore: add -y/--yes flag to skip interactive prompt (#1525) #5670

chore: add -y/--yes flag to skip interactive prompt (#1525)

chore: add -y/--yes flag to skip interactive prompt (#1525) #5670

Workflow file for this run

name: Pull Request
on:
pull_request:
branches:
- main
types: [opened, synchronize, reopened, labeled]
push:
branches:
- main
permissions:
id-token: write
contents: read
env:
REGISTRY: gcr.io/linen-analyst-344721 #gcr for speakeasy-common gcr registry
DOCKER_REPOSITORY_OWNER: ${{github.repository_owner}}
NODE_OPTIONS: "--max-old-space-size=5120"
jobs:
changes:
name: Tag branch changes
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: "read"
pull-requests: read
outputs:
server: ${{ steps.gates.outputs.server }}
client: ${{ steps.gates.outputs.client }}
cli: ${{ steps.gates.outputs.cli }}
functions: ${{ steps.gates.outputs.functions }}
tsframework: ${{ steps.gates.outputs.tsframework }}
elements: ${{ steps.gates.outputs.elements }}
migrations: ${{ steps.gates.outputs.migrations }}
plog: ${{ steps.gates.outputs.plog }}
steps:
- name: Checkout source code
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
name: Check for changed packages
id: filter
with:
filters: .github/filters.yaml
- id: gates
name: Set outputs
env:
HAS_PREVIEW_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'preview') }}
run: |
if [[ "${{ steps.filter.outputs.server }}" == "true" || "${{ github.ref }}" == "refs/heads/main" || "$HAS_PREVIEW_LABEL" == "true" ]]; then
echo "server=true" >> $GITHUB_OUTPUT
echo "Server jobs will run."
else
echo "Server jobs will be skipped."
fi
if [[ "${{ steps.filter.outputs.client }}" == "true" || "${{ github.ref }}" == "refs/heads/main" || "$HAS_PREVIEW_LABEL" == "true" ]]; then
echo "client=true" >> $GITHUB_OUTPUT
echo "Client jobs will run."
else
echo "Client jobs will be skipped."
fi
if [[ "${{ steps.filter.outputs.cli }}" == "true" || "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "cli=true" >> $GITHUB_OUTPUT
echo "CLI jobs will run."
else
echo "CLI jobs will be skipped."
fi
if [[ "${{ steps.filter.outputs.functions }}" == "true" || "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "functions=true" >> $GITHUB_OUTPUT
echo "Functions jobs will run."
else
echo "Functions jobs will be skipped."
fi
if [[ "${{ steps.filter.outputs.tsframework }}" == "true" || "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "tsframework=true" >> $GITHUB_OUTPUT
echo "TypeScript framework jobs will run."
else
echo "TypeScript framework jobs will be skipped."
fi
if [[ "${{ steps.filter.outputs.elements }}" == "true" || "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "elements=true" >> $GITHUB_OUTPUT
echo "Elements jobs will run."
else
echo "Elements jobs will be skipped."
fi
if [[ "${{ steps.filter.outputs.migrations }}" == "true" ]]; then
echo "migrations=true" >> $GITHUB_OUTPUT
echo "Migrations jobs will run."
else
echo "Migrations jobs will be skipped."
fi
if [[ "${{ steps.filter.outputs.plog }}" == "true" || "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "plog=true" >> $GITHUB_OUTPUT
echo "Plog jobs will run."
else
echo "Plog jobs will be skipped."
fi
docker-build-server:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [changes, server-build-lint]
env:
GOMAXPROCS: 4
steps:
- name: Skip if no server changes exist
if: ${{ needs.changes.outputs.server != 'true' }}
run: echo "No server changes detected — skipping server-build-lint job."
- name: Checkout
if: ${{ needs.changes.outputs.server == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Download server build artifact
if: ${{ needs.changes.outputs.server == 'true' }}
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: server-bin
path: server/bin
- id: "auth"
if: ${{ needs.changes.outputs.server == 'true' }}
name: "Authenticate to Google Cloud"
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
token_format: "access_token"
workload_identity_provider: "projects/409661704476/locations/global/workloadIdentityPools/ga-pool/providers/github-oidc-provider"
service_account: "speakeasy-registry-ga-ci@linen-analyst-344721.iam.gserviceaccount.com"
- name: Login to GCR
if: ${{ needs.changes.outputs.server == 'true' }}
env:
GCR_TOKEN: ${{ steps.auth.outputs.access_token }}
run: |
max_attempts=3
for ((i=1; i<=max_attempts; i++)); do
echo "Docker login attempt $i/$max_attempts..."
if docker login -u oauth2accesstoken --password-stdin gcr.io/linen-analyst-344721 <<< "$GCR_TOKEN"; then
echo "Login successful"
exit 0
fi
if [ $i -lt $max_attempts ]; then
echo "Login failed, waiting 5s before retry..."
sleep 5
fi
done
echo "All login attempts failed"
exit 1
- name: Build and Push Registry image to GCR
id: build
if: ${{ needs.changes.outputs.server == 'true' }}
uses: ./.github/workflows/composite/build-push
with:
registry: ${{ env.REGISTRY }}
username: oauth2accesstoken
password: ${{ steps.auth.outputs.access_token }}
image: ${{ env.DOCKER_REPOSITORY_OWNER }}/gram
context: ./server
file: server/Dockerfile
git-auth-token: ${{ secrets.BOT_REPO_TOKEN }}
build-args: |
GIT_USERNAME=speakeasybot
- name: Pull and Run Image
if: ${{ needs.changes.outputs.server == 'true' }}
run: |
echo "Pulling image: ${{ steps.build.outputs.image-tag }}"
docker pull ${{ steps.build.outputs.image-tag }}
echo "Running image..."
docker run --rm ${{ steps.build.outputs.image-tag }} version
atlas-lint:
name: Lint migrations with Atlas
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
if: needs.changes.outputs.migrations == 'true' && github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
services:
postgres:
image: pgvector/pgvector:pg17
env:
POSTGRES_DB: dev
POSTGRES_PASSWORD: pass
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-start-period 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
with:
fetch-depth: 0
- name: Setup Atlas
uses: ariga/setup-atlas@1ffa6444093025c9791791f0de623560359acd02 # v0
with:
cloud-token: ${{ secrets.ATLAS_TOKEN }}
- name: Lint PostgreSQL migrations
uses: ariga/atlas-action/migrate/lint@v1.13.10 # ffcda7674f642bd28873a37bee3a47f5631e72d3
with:
dir: file://server/migrations
dir-name: gram
dev-url: postgres://postgres:pass@localhost:5432/dev?search_path=public&sslmode=disable
config: file://server/atlas.hcl
git-base: origin/main
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Lint ClickHouse migrations
uses: ariga/atlas-action/migrate/lint@v1.13.10 # ffcda7674f642bd28873a37bee3a47f5631e72d3
with:
dir: file://server/clickhouse/migrations
dir-name: gram-clickhouse
dev-url: docker://clickhouse/clickhouse-server/25.8.3/dev
git-base: origin/main
env:
GITHUB_TOKEN: ${{ github.token }}
atlas-push:
name: Push migrations to Atlas Registry
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [changes, server-build-lint]
if: needs.changes.outputs.server == 'true'
permissions:
contents: read
pull-requests: write
concurrency:
group: atlas-push-${{ github.ref }}
cancel-in-progress: true
services:
postgres:
image: pgvector/pgvector:pg17
env:
POSTGRES_DB: dev
POSTGRES_PASSWORD: pass
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-start-period 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
with:
fetch-depth: 0
- name: Setup Atlas
uses: ariga/setup-atlas@1ffa6444093025c9791791f0de623560359acd02 # v0
with:
cloud-token: ${{ secrets.ATLAS_TOKEN }}
- name: Generate tag
id: tag
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
SHORT_SHA=$(echo "${{ github.event.pull_request.head.sha }}" | cut -c1-8)
echo "tag=pr-${{ github.event.pull_request.number }}-${SHORT_SHA}" >> $GITHUB_OUTPUT
else
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
echo "tag=sha-${SHORT_SHA}" >> $GITHUB_OUTPUT
fi
- name: Push PostgreSQL migrations
uses: ariga/atlas-action/migrate/push@v1.13.10 # ffcda7674f642bd28873a37bee3a47f5631e72d3
with:
dir: file://server/migrations
dir-name: gram
dev-url: postgres://postgres:pass@localhost:5432/dev?search_path=public&sslmode=disable
tag: ${{ steps.tag.outputs.tag }}
- name: Push ClickHouse migrations
uses: ariga/atlas-action/migrate/push@v1.13.10 # ffcda7674f642bd28873a37bee3a47f5631e72d3
with:
dir: file://server/clickhouse/migrations
dir-name: gram-clickhouse
dev-url: docker://clickhouse/clickhouse-server/25.8.3/dev
tag: ${{ steps.tag.outputs.tag }}
server-build-lint:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
env:
GOMAXPROCS: 4
services:
postgres:
image: pgvector/pgvector:pg17
env:
POSTGRES_USER: gram
POSTGRES_PASSWORD: gram
POSTGRES_DB: gram
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5439:5432
steps:
- name: Skip if no server changes exist
if: ${{ needs.changes.outputs.server != 'true' }}
run: echo "No server changes detected — skipping server-build-lint job."
- name: Checkout
if: ${{ needs.changes.outputs.server == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.server == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.server == 'true' }}
run: mise run github
- name: Cache Go
if: ${{ needs.changes.outputs.server == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_GO_KEY }}
restore-keys: |
${{ env.GH_CACHE_GO_KEY }}
${{ env.GH_CACHE_GO_KEY_PARTIAL }}
path: |
${{ env.GOCACHE }}
${{ env.GOMODCACHE }}
- name: Build
if: ${{ needs.changes.outputs.server == 'true' }}
run: mise run build:server --readonly
- name: Lint with golangci-lint
if: ${{ needs.changes.outputs.server == 'true' }}
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
install-mode: none
working-directory: server
- name: Upload server build
if: ${{ needs.changes.outputs.server == 'true' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: server-bin
path: server/bin
retention-days: 2
- name: Install Go tools
if: ${{ needs.changes.outputs.server == 'true' }}
run: go install tool
working-directory: server
# TODO add speakeasy cli so the sdk can be generated
- name: Run code generators
if: ${{ needs.changes.outputs.server == 'true' }}
run: mise run "gen:server"
- name: Run "go mod tidy"
if: ${{ needs.changes.outputs.server == 'true' }}
run: mise run go:tidy
- name: Setup Atlas
if: ${{ needs.changes.outputs.server == 'true' }}
uses: ariga/setup-atlas@1ffa6444093025c9791791f0de623560359acd02 # v0
with:
cloud-token: ${{ secrets.ATLAS_TOKEN }}
- name: Run migrations
if: ${{ needs.changes.outputs.server == 'true' }}
run: mise run db:migrate
- name: Diff database with schema
if: ${{ needs.changes.outputs.server == 'true' }}
run: mise run db:diff out-of-sync
- name: Check for dirty files
if: ${{ needs.changes.outputs.server == 'true' }}
run: |
mise run db:migrate --dry
mise run git:porcelain
cli-build-lint:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
env:
GOMAXPROCS: 4
services:
postgres:
image: pgvector/pgvector:pg17
env:
POSTGRES_USER: gram
POSTGRES_PASSWORD: gram
POSTGRES_DB: gram
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5439:5432
steps:
- name: Skip if no cli changes exist
if: ${{ needs.changes.outputs.cli != 'true' }}
run: echo "No cli changes detected — skipping cli-build-lint job."
- name: Checkout
if: ${{ needs.changes.outputs.cli == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.cli == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.cli == 'true' }}
run: mise run github
- name: Cache Go
if: ${{ needs.changes.outputs.cli == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_GO_KEY }}
restore-keys: |
${{ env.GH_CACHE_GO_KEY }}
${{ env.GH_CACHE_GO_KEY_PARTIAL }}
path: |
${{ env.GOCACHE }}
${{ env.GOMODCACHE }}
- name: Build
if: ${{ needs.changes.outputs.cli == 'true' }}
run: mise run build:cli --readonly
- name: Lint with golangci-lint
if: ${{ needs.changes.outputs.cli == 'true' }}
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
install-mode: none
working-directory: cli
- name: Install Go tools
if: ${{ needs.changes.outputs.cli == 'true' }}
run: go install tool
working-directory: cli
# TODO add speakeasy cli so the sdk can be generated
- name: Run code generators
if: ${{ needs.changes.outputs.cli == 'true' }}
run: mise run "gen:server"
- name: Run "go mod tidy"
if: ${{ needs.changes.outputs.cli == 'true' }}
run: mise run go:tidy
- name: Check for dirty files
if: ${{ needs.changes.outputs.cli == 'true' }}
run: mise run git:porcelain
plog-build-lint:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
env:
GOMAXPROCS: 4
steps:
- name: Skip if no plog changes exist
if: ${{ needs.changes.outputs.plog != 'true' }}
run: echo "No plog changes detected — skipping plog-build-lint job."
- name: Checkout
if: ${{ needs.changes.outputs.plog == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.plog == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.plog == 'true' }}
run: mise run github
- name: Cache Go
if: ${{ needs.changes.outputs.plog == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_GO_KEY }}
restore-keys: |
${{ env.GH_CACHE_GO_KEY }}
${{ env.GH_CACHE_GO_KEY_PARTIAL }}
path: |
${{ env.GOCACHE }}
${{ env.GOMODCACHE }}
- name: Install Go tools
if: ${{ needs.changes.outputs.plog == 'true' }}
run: go install tool
working-directory: plog
- name: Build
if: ${{ needs.changes.outputs.plog == 'true' }}
run: mise run build:plog --readonly
- name: Lint with golangci-lint
if: ${{ needs.changes.outputs.plog == 'true' }}
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
install-mode: none
working-directory: plog
- name: Test
if: ${{ needs.changes.outputs.plog == 'true' }}
run: mise run test:plog
- name: Run "go mod tidy"
if: ${{ needs.changes.outputs.plog == 'true' }}
run: mise run go:tidy
- name: Check for dirty files
if: ${{ needs.changes.outputs.plog == 'true' }}
run: mise run git:porcelain
server-test:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
env:
GOMAXPROCS: 4
steps:
- name: Skip if no server changes exist
if: ${{ needs.changes.outputs.server != 'true' }}
run: echo "No server changes detected — skipping server-test job."
- name: Checkout
if: ${{ needs.changes.outputs.server == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.server == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.server == 'true' }}
run: mise run github
- name: Cache Go
if: ${{ needs.changes.outputs.server == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_GO_KEY }}
restore-keys: |
${{ env.GH_CACHE_GO_KEY }}
${{ env.GH_CACHE_GO_KEY_PARTIAL }}
path: |
${{ env.GOCACHE }}
${{ env.GOMODCACHE }}
- name: Install Go deps
if: ${{ needs.changes.outputs.server == 'true' }}
run: mise run install:go
- name: Test
if: ${{ needs.changes.outputs.server == 'true' }}
run: mise run test:server
docker-build-client:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
env:
GRAM_GIT_SHA: "${{ github.sha }}"
# Build dashboard docker image when server OR client changes exist (needed for preview environments)
SHOULD_BUILD_DOCKER: ${{ needs.changes.outputs.client == 'true' || needs.changes.outputs.server == 'true' }}
steps:
- name: Skip if no relevant changes exist
if: ${{ env.SHOULD_BUILD_DOCKER != 'true' }}
run: echo "No server or client changes detected — skipping dashboard build job."
- name: Checkout
if: ${{ env.SHOULD_BUILD_DOCKER == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ env.SHOULD_BUILD_DOCKER == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ env.SHOULD_BUILD_DOCKER == 'true' }}
run: mise run github
- name: Cache PNPM
if: ${{ env.SHOULD_BUILD_DOCKER == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}
- name: Install dependencies
if: ${{ env.SHOULD_BUILD_DOCKER == 'true' }}
run: pnpm install --frozen-lockfile
- name: Build
if: ${{ env.SHOULD_BUILD_DOCKER == 'true' }}
env:
NODE_ENV: production
run: mise exec --env viteprod -- pnpm build
- name: Upload source maps to DataDog
if: ${{ env.SHOULD_BUILD_DOCKER == 'true' }}
env:
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
run: mise run datadog:sourcemaps --git-sha "${{ github.sha }}"
- id: "auth"
if: env.SHOULD_BUILD_DOCKER == 'true'
name: "Authenticate to Google Cloud"
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
token_format: "access_token"
workload_identity_provider: "projects/409661704476/locations/global/workloadIdentityPools/ga-pool/providers/github-oidc-provider"
service_account: "speakeasy-registry-ga-ci@linen-analyst-344721.iam.gserviceaccount.com"
- name: Login to GCR
if: env.SHOULD_BUILD_DOCKER == 'true'
env:
GCR_TOKEN: ${{ steps.auth.outputs.access_token }}
run: |
max_attempts=3
for ((i=1; i<=max_attempts; i++)); do
echo "Docker login attempt $i/$max_attempts..."
if docker login -u oauth2accesstoken --password-stdin gcr.io/linen-analyst-344721 <<< "$GCR_TOKEN"; then
echo "Login successful"
exit 0
fi
if [ $i -lt $max_attempts ]; then
echo "Login failed, waiting 5s before retry..."
sleep 5
fi
done
echo "All login attempts failed"
exit 1
- name: Build and Push Registry image to GCR
id: build
if: env.SHOULD_BUILD_DOCKER == 'true'
uses: ./.github/workflows/composite/build-push
with:
registry: ${{ env.REGISTRY }}
username: oauth2accesstoken
password: ${{ steps.auth.outputs.access_token }}
image: ${{ env.DOCKER_REPOSITORY_OWNER }}/gram-dashboard
context: ./client/dashboard
file: client/dashboard/Dockerfile
git-auth-token: ${{ secrets.BOT_REPO_TOKEN }}
- name: Prune PNPM store
if: ${{ env.SHOULD_BUILD_DOCKER == 'true' && success() }}
run: pnpm store prune
client-build-lint:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
steps:
- name: Skip if no client changes exist
if: ${{ needs.changes.outputs.client != 'true' }}
run: echo "No client changes detected — skipping client-lint-test job."
- name: Checkout
if: ${{ needs.changes.outputs.client == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.client == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.client == 'true' }}
run: mise run github
- name: Cache PNPM
if: ${{ needs.changes.outputs.client == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}
- name: Install dependencies
if: ${{ needs.changes.outputs.client == 'true' }}
run: pnpm install --frozen-lockfile
- name: Build
if: ${{ needs.changes.outputs.client == 'true' }}
env:
NODE_ENV: production
run: mise exec --env viteprod -- pnpm build
- name: Lint
if: ${{ needs.changes.outputs.client == 'true' }}
run: pnpm lint
working-directory: client/dashboard
- name: Check for dirty files
if: ${{ needs.changes.outputs.client == 'true' }}
run: mise run git:porcelain
- name: Prune PNPM store
if: ${{ needs.changes.outputs.client == 'true' && success() }}
run: pnpm store prune
ts-sdk-build-lint:
strategy:
matrix:
target: ["client/sdk"]
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
steps:
- name: Skip if no client changes exist
if: ${{ needs.changes.outputs.client != 'true' }}
run: echo "No client changes detected — skipping ts-sdk-build-lint."
- name: Checkout
if: ${{ needs.changes.outputs.client == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.client == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.client == 'true' }}
run: mise run github
- name: Cache PNPM
if: ${{ needs.changes.outputs.client == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}
- name: Install dependencies
if: ${{ needs.changes.outputs.client == 'true' }}
run: pnpm install --frozen-lockfile
- name: Build
if: ${{ needs.changes.outputs.client == 'true' }}
run: pnpm build
working-directory: ${{ matrix.target }}
- name: Lint
if: ${{ needs.changes.outputs.client == 'true' }}
run: pnpm lint
working-directory: ${{ matrix.target }}
- name: Prune PNPM store
if: ${{ needs.changes.outputs.client == 'true' && success() }}
run: pnpm store prune
- name: Check for dirty files
if: ${{ needs.changes.outputs.client == 'true' }}
run: mise run git:porcelain
functions-build-host:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
steps:
- name: Skip if no functions changes exist
if: ${{ needs.changes.outputs.functions != 'true' }}
run: echo "No functions changes detected — skipping functions-build-bin."
- name: Checkout
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.functions == 'true' }}
run: mise run github
- name: Cache Go
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_GO_KEY }}
restore-keys: |
${{ env.GH_CACHE_GO_KEY }}
${{ env.GH_CACHE_GO_KEY_PARTIAL }}
path: |
${{ env.GOCACHE }}
${{ env.GOMODCACHE }}
- name: Cache PNPM
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}
- name: Install Go tools
if: ${{ needs.changes.outputs.functions == 'true' }}
run: go install tool
- name: Install dependencies
if: ${{ needs.changes.outputs.functions == 'true' }}
run: pnpm install --frozen-lockfile
- name: Test functions runner
working-directory: functions
if: ${{ needs.changes.outputs.functions == 'true' }}
run: mise run test:functions
- name: Test functions entrypoint
working-directory: functions
if: ${{ needs.changes.outputs.functions == 'true' }}
run: pnpm test
- name: Type check functions entrypoint
working-directory: functions
if: ${{ needs.changes.outputs.functions == 'true' }}
run: pnpm type-check
- name: Lint Go server
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
install-mode: none
working-directory: functions
- name: Test Go server
if: ${{ needs.changes.outputs.functions == 'true' }}
run: mise run test:functions
- name: Build Gram Functions host
if: ${{ needs.changes.outputs.functions == 'true' }}
env:
MELANGE_PRIVATE_KEY: ${{ secrets.MELANGE_PRIVATE_KEY }}
run: |
KEY_DIR=$(mktemp -d)
KEY_FILE="$KEY_DIR/melange-signing-key.rsa"
echo "$MELANGE_PRIVATE_KEY" > "$KEY_FILE"
mise run build:functions-bin --melange-private-key "$KEY_FILE"
rm -rf "$KEY_DIR"
- name: Upload melange packages artifact
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: functions-packages
path: functions/packages
retention-days: 2
- name: Run "go mod tidy"
if: ${{ needs.changes.outputs.functions == 'true' }}
run: mise run go:tidy
- name: Check for dirty files
if: ${{ needs.changes.outputs.functions == 'true' }}
run: mise run git:porcelain
- name: Prune PNPM store
if: ${{ needs.changes.outputs.functions == 'true' && success() }}
run: pnpm store prune
functions-image:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [changes, functions-build-host]
strategy:
matrix:
runtime: ["nodejs22"]
steps:
- name: Skip if no functions changes exist
if: ${{ needs.changes.outputs.functions != 'true' }}
run: echo "No functions changes detected — skipping functions-image (${{ matrix.runtime }})."
- name: Checkout
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.functions == 'true' }}
run: mise run github
- name: Cache PNPM
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}
- name: Install dependencies
if: ${{ needs.changes.outputs.functions == 'true' }}
run: pnpm install --frozen-lockfile
- name: Download melange packages artifact
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: functions-packages
path: functions/packages
- name: Build apko image (${{ matrix.runtime }})
id: apko
if: ${{ needs.changes.outputs.functions == 'true' }}
env:
MELANGE_PUBLIC_KEY: ${{ vars.MELANGE_PUBLIC_KEY }}
run: |
KEY_DIR=$(mktemp -d)
KEY_FILE="$KEY_DIR/melange-signing-key.rsa.pub"
echo "$MELANGE_PUBLIC_KEY" > "$KEY_FILE"
mise run build:functions-image \
--melange-public-key "$KEY_FILE" \
--apko-config ./images/${{ matrix.runtime }}-alpine3.22.yaml \
--image "apko-${{ matrix.runtime }}:${{ github.sha }}" \
--tarball-name ${{ matrix.runtime }} \
--out oci/${{ matrix.runtime }}
- name: Run safety checks
if: ${{ needs.changes.outputs.functions == 'true' }}
run: |
mise run test:apko-functions \
--image ${{ steps.apko.outputs.image }} \
--tarball ${{ steps.apko.outputs.tarball }}
- name: "[DEV] Create fly app"
id: flyCreateDev
if: ${{ needs.changes.outputs.functions == 'true' }}
shell: bash
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_DEV }}
FLY_IMAGE: ${{ vars.FLY_IMAGE_DEV }}
run: |
mise run fly:init-runner \
--org ${{ secrets.FLY_ORG_DEV }} \
--image $FLY_IMAGE \
--force
- name: "[PROD] Create fly app"
id: flyCreateProd
if: ${{ needs.changes.outputs.functions == 'true' }}
shell: bash
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_PROD }}
FLY_IMAGE: ${{ vars.FLY_IMAGE_PROD }}
run: |
mise run fly:init-runner \
--org ${{ secrets.FLY_ORG_PROD }} \
--image $FLY_IMAGE \
--force
- name: Set up Docker Buildx
if: ${{ needs.changes.outputs.functions == 'true' }}
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- name: Extract Docker metadata
if: ${{ needs.changes.outputs.functions == 'true' }}
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
images: |
${{ steps.flyCreateDev.outputs.flyAppRegistry }}
${{ steps.flyCreateProd.outputs.flyAppRegistry }}
tags: |
type=ref,event=branch,prefix=,suffix=-${{ matrix.runtime }}
type=ref,event=tag,prefix=,suffix=-${{ matrix.runtime }}
type=schedule,pattern=nightly,prefix=,suffix=-${{ matrix.runtime }}
type=sha,prefix=,suffix=-${{ matrix.runtime }}
type=sha,format=long,prefix=,suffix=-${{ matrix.runtime }}
- name: Tag images
if: ${{ needs.changes.outputs.functions == 'true' }}
working-directory: functions
run: |
set -x
echo "Loading image from tarball: ${{ steps.apko.outputs.tarball }}"
docker image load -i ${{ steps.apko.outputs.tarball }}
TAGS="${{ steps.meta.outputs.tags }}"
for TAG in $TAGS; do
docker tag "${{ steps.apko.outputs.image }}-amd64" "$TAG"
done
- name: "[DEV] Push images"
if: ${{ needs.changes.outputs.functions == 'true' }}
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_DEV }}
run: |
set -x
fly auth docker
docker push --all-tags "${{ steps.flyCreateDev.outputs.flyAppRegistry }}"
- name: "[PROD] Push images"
if: ${{ needs.changes.outputs.functions == 'true' }}
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_PROD }}
run: |
set -x
fly auth docker
docker push --all-tags "${{ steps.flyCreateProd.outputs.flyAppRegistry }}"
- name: Prune PNPM store
if: ${{ needs.changes.outputs.functions == 'true' && success() }}
run: pnpm store prune
ts-framework-test:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [changes]
steps:
- name: Skip if no ts framework changes exist
if: ${{ needs.changes.outputs.tsframework != 'true' }}
run: echo "No TypeScript framework changes detected — skipping ts-framework-test."
- name: Checkout
if: ${{ needs.changes.outputs.tsframework == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.tsframework == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.tsframework == 'true' }}
run: mise run github
- name: Cache PNPM
if: ${{ needs.changes.outputs.tsframework == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}
- name: Install dependencies
if: ${{ needs.changes.outputs.tsframework == 'true' }}
run: pnpm install --frozen-lockfile
- name: Test TypeScript framework
if: ${{ needs.changes.outputs.tsframework == 'true' }}
run: pnpm --filter "./ts-framework/**" test
- name: Build TypeScript framework
if: ${{ needs.changes.outputs.tsframework == 'true' }}
run: pnpm --filter "./ts-framework/**" build
- name: Prune PNPM store
if: ${{ needs.changes.outputs.tsframework == 'true' && success() }}
run: pnpm store prune
elements-build-lint:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
env:
GRAM_GIT_SHA: "${{ github.sha }}"
steps:
- name: Skip if no elements changes exist
if: ${{ needs.changes.outputs.elements != 'true' }}
run: echo "No elements changes detected — skipping elements-build-lint."
- name: Checkout
if: ${{ needs.changes.outputs.elements == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.elements == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.elements == 'true' }}
run: mise run github
- name: Cache PNPM
if: ${{ needs.changes.outputs.elements == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}
- name: Install dependencies
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm install --frozen-lockfile
- name: Build
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm build
working-directory: elements
- name: Lint
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm lint
working-directory: elements
- name: Type check
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm type-check
working-directory: elements
- name: Prune PNPM store
if: ${{ needs.changes.outputs.elements == 'true' && success() }}
run: pnpm store prune
elements-test:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changes
steps:
- name: Skip if no elements changes exist
if: ${{ needs.changes.outputs.elements != 'true' }}
run: echo "No elements changes detected — skipping elements-test."
- name: Checkout
if: ${{ needs.changes.outputs.elements == 'true' }}
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
- name: Setup Mise
if: ${{ needs.changes.outputs.elements == 'true' }}
uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
with:
install: true
cache: true
env: false
- name: Prepare GitHub Actions environment
if: ${{ needs.changes.outputs.elements == 'true' }}
run: mise run github
- name: Cache PNPM
if: ${{ needs.changes.outputs.elements == 'true' }}
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
key: ${{ env.GH_CACHE_PNPM_KEY }}
restore-keys: |
${{ env.GH_CACHE_PNPM_KEY }}
${{ env.GH_CACHE_PNPM_KEY_PARTIAL }}
path: |
${{ env.PNPM_STORE_PATH }}
- name: Install dependencies
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm install --frozen-lockfile
- name: Test
if: ${{ needs.changes.outputs.elements == 'true' }}
run: pnpm test
working-directory: elements
- name: Prune PNPM store
if: ${{ needs.changes.outputs.elements == 'true' && success() }}
run: pnpm store prune
dispatch-release:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [docker-build-server]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Dispatch prepare-release
env:
GH_TOKEN: ${{ secrets.GRAM_INFRA_SPEAKEASYBOT_TOKEN }}
run: |
gh workflow run "Prepare Release" \
-R speakeasy-api/gram-infra \
-f environment=dev \
-f commit_hash=${{ github.sha }}
preview-label:
runs-on: blacksmith-2vcpu-ubuntu-2404
needs: [changes, docker-build-server, docker-build-client]
if: |
github.event_name == 'pull_request' &&
!github.event.pull_request.head.repo.fork &&
(needs.changes.outputs.server == 'true' || needs.changes.outputs.client == 'true')
permissions:
pull-requests: write
steps:
- name: Add preview label
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.payload.pull_request.number,
labels: ['preview']
});