Skip to content

feat(token_lite): single-game gas-optimized token component (denshokan lite) #210

feat(token_lite): single-game gas-optimized token component (denshokan lite)

feat(token_lite): single-game gas-optimized token component (denshokan lite) #210

Workflow file for this run

name: PR CI
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
concurrency:
group: pr-ci-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
changes:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
packages: ${{ steps.filter.outputs.packages }}
has_tests: ${{ steps.compute-matrix.outputs.has_tests }}
need_egs: ${{ steps.compute-matrix.outputs.need_egs }}
need_metagame: ${{ steps.compute-matrix.outputs.need_metagame }}
need_economy: ${{ steps.compute-matrix.outputs.need_economy }}
need_utilities: ${{ steps.compute-matrix.outputs.need_utilities }}
need_presets: ${{ steps.compute-matrix.outputs.need_presets }}
infra_ci: ${{ steps.filter.outputs.infra_ci }}
general_ci: ${{ steps.filter.outputs.general_ci }}
steps:
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
packages:
- "packages/**"
- "Scarb.toml"
- "Scarb.lock"
- ".tool-versions"
general_ci:
- "codecov.yml"
- ".tool-versions"
infra_ci:
- ".github/workflows/**"
- ".github/prompts/**"
- ".github/pull_request_template.md"
embeddable_game_standard:
- "packages/embeddable_game_standard/**"
metagame_pkg:
- "packages/metagame/**"
economy:
- "packages/economy/**"
utilities:
- "packages/utilities/**"
interfaces:
- "packages/interfaces/**"
presets:
- "packages/presets/**"
testing:
- "packages/testing/**"
test_common:
- "packages/test_common/**"
tool_versions:
- ".tool-versions"
workspace_manifests:
- "Scarb.toml"
- "Scarb.lock"
- name: Compute test matrix from changed packages
id: compute-matrix
env:
FILTER_INTERFACES: ${{ steps.filter.outputs.interfaces }}
FILTER_TESTING: ${{ steps.filter.outputs.testing }}
FILTER_UTILITIES: ${{ steps.filter.outputs.utilities }}
FILTER_METAGAME_PKG: ${{ steps.filter.outputs.metagame_pkg }}
FILTER_ECONOMY: ${{ steps.filter.outputs.economy }}
FILTER_EGS: ${{ steps.filter.outputs.embeddable_game_standard }}
FILTER_TEST_COMMON: ${{ steps.filter.outputs.test_common }}
FILTER_PRESETS: ${{ steps.filter.outputs.presets }}
FILTER_TOOL_VERSIONS: ${{ steps.filter.outputs.tool_versions }}
FILTER_WORKSPACE_MANIFESTS: ${{ steps.filter.outputs.workspace_manifests }}
run: |
# Determine which group packages need testing based on transitive deps
# interfaces & testing & .tool-versions → ALL
# utilities → utilities + metagame + embeddable_game_standard + presets
# metagame_pkg → metagame + embeddable_game_standard + presets
# economy → economy + presets
# embeddable_game_standard → embeddable_game_standard + presets
# test_common → embeddable_game_standard + metagame + presets
# presets → presets
RUN_ALL="false"
NEED_EGS="false"
NEED_METAGAME="false"
NEED_ECONOMY="false"
NEED_UTILITIES="false"
NEED_PRESETS="false"
if [ "$FILTER_INTERFACES" = "true" ] || [ "$FILTER_TESTING" = "true" ] || [ "$FILTER_TOOL_VERSIONS" = "true" ] || [ "$FILTER_WORKSPACE_MANIFESTS" = "true" ]; then
RUN_ALL="true"
fi
if [ "$RUN_ALL" != "true" ]; then
if [ "$FILTER_UTILITIES" = "true" ]; then
NEED_UTILITIES="true"
NEED_METAGAME="true"
NEED_EGS="true"
NEED_PRESETS="true"
fi
if [ "$FILTER_METAGAME_PKG" = "true" ]; then
NEED_METAGAME="true"
NEED_EGS="true"
NEED_PRESETS="true"
fi
if [ "$FILTER_ECONOMY" = "true" ]; then
NEED_ECONOMY="true"
NEED_PRESETS="true"
fi
if [ "$FILTER_EGS" = "true" ]; then
NEED_EGS="true"
NEED_PRESETS="true"
fi
if [ "$FILTER_TEST_COMMON" = "true" ]; then
NEED_EGS="true"
NEED_METAGAME="true"
NEED_PRESETS="true"
fi
if [ "$FILTER_PRESETS" = "true" ]; then
NEED_PRESETS="true"
fi
fi
if [ "$RUN_ALL" = "true" ]; then
NEED_EGS="true"
NEED_METAGAME="true"
NEED_ECONOMY="true"
NEED_UTILITIES="true"
NEED_PRESETS="true"
fi
HAS_TESTS="false"
for flag in "$NEED_EGS" "$NEED_METAGAME" "$NEED_ECONOMY" "$NEED_UTILITIES" "$NEED_PRESETS"; do
if [ "$flag" = "true" ]; then
HAS_TESTS="true"
break
fi
done
echo "has_tests=$HAS_TESTS" >> "$GITHUB_OUTPUT"
echo "need_egs=$NEED_EGS" >> "$GITHUB_OUTPUT"
echo "need_metagame=$NEED_METAGAME" >> "$GITHUB_OUTPUT"
echo "need_economy=$NEED_ECONOMY" >> "$GITHUB_OUTPUT"
echo "need_utilities=$NEED_UTILITIES" >> "$GITHUB_OUTPUT"
echo "need_presets=$NEED_PRESETS" >> "$GITHUB_OUTPUT"
echo "Packages to test: egs=$NEED_EGS metagame=$NEED_METAGAME economy=$NEED_ECONOMY utilities=$NEED_UTILITIES presets=$NEED_PRESETS"
infra-validate:
needs: changes
if: needs.changes.outputs.infra_ci == 'true' || needs.changes.outputs.general_ci == 'true'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
- name: Validate workflow YAML
run: |
YAML_CLI="yaml@2.8.1"
for file in .github/workflows/*.yml .github/workflows/*.yaml; do
[ -f "$file" ] || continue
npx -y "$YAML_CLI" valid < "$file"
done
if [ -f codecov.yml ]; then
npx -y "$YAML_CLI" valid < codecov.yml
fi
- name: Validate package count matches codecov config
run: |
matrix_count=$(grep -cE '^\s+module:' .github/workflows/main-ci.yml)
codecov_count=$(grep 'after_n_builds:' codecov.yml | grep -oE '[0-9]+')
echo "Matrix modules: $matrix_count"
echo "Codecov after_n_builds: $codecov_count"
if [ "$matrix_count" != "$codecov_count" ]; then
echo "::error::Matrix has $matrix_count modules but codecov.yml expects $codecov_count builds"
echo "::error::Update codecov.yml after_n_builds to match module count"
exit 1
fi
echo "Configuration validated: $matrix_count modules"
lint:
needs: changes
if: needs.changes.outputs.packages == 'true'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
- name: Setup Scarb
uses: software-mansion/setup-scarb@v1
with:
tool-versions: .tool-versions
cache: false
- name: Scarb fmt
run: scarb fmt --check --workspace
setup:
needs: [changes, lint]
if: needs.changes.outputs.has_tests == 'true'
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
test_matrix: ${{ steps.build-matrix.outputs.test_matrix }}
steps:
- uses: actions/checkout@v6
- name: Build test matrix from package flags
id: build-matrix
env:
NEED_EGS: ${{ needs.changes.outputs.need_egs }}
NEED_METAGAME: ${{ needs.changes.outputs.need_metagame }}
NEED_ECONOMY: ${{ needs.changes.outputs.need_economy }}
NEED_UTILITIES: ${{ needs.changes.outputs.need_utilities }}
NEED_PRESETS: ${{ needs.changes.outputs.need_presets }}
run: |
# Build JSON matrix from per-package boolean flags
# Module catalog must match main-ci.yml
INCLUDES="[]"
add() { INCLUDES=$(echo "$INCLUDES" | jq -c --arg p "$1" --arg m "$2" --arg r "$3" --argjson f "$4" '. + [{package:$p,module:$m,runner:$r,fuzzer_runs:$f}]'); }
if [ "$NEED_EGS" = "true" ]; then
add game_components_embeddable_game_standard token ubuntu-latest-32 32
add game_components_embeddable_game_standard minigame ubuntu-latest-8 32
add game_components_embeddable_game_standard metagame ubuntu-latest-8 32
add game_components_embeddable_game_standard registry ubuntu-latest-8 32
add game_components_embeddable_game_standard token_lite ubuntu-latest-8 32
fi
if [ "$NEED_METAGAME" = "true" ]; then
add game_components_metagame leaderboard ubuntu-latest-4 256
add game_components_metagame registration ubuntu-latest-4 256
add game_components_metagame entry_requirement ubuntu-latest-4 256
add game_components_metagame entry_fee ubuntu-latest-4 256
add game_components_metagame prize ubuntu-latest-4 256
add game_components_metagame ticket_booth ubuntu-latest-4 256
add game_components_metagame merkledrop ubuntu-latest-4 256
fi
if [ "$NEED_ECONOMY" = "true" ]; then
add game_components_economy tokenomics ubuntu-latest-4 256
fi
if [ "$NEED_UTILITIES" = "true" ]; then
add game_components_utilities math ubuntu-latest-4 256
add game_components_utilities distribution ubuntu-latest-4 256
add game_components_utilities utils ubuntu-latest-4 256
add game_components_utilities renderer ubuntu-latest-4 256
fi
if [ "$NEED_PRESETS" = "true" ]; then
add game_components_presets presets ubuntu-latest-4 256
fi
MATRIX=$(echo "$INCLUDES" | jq -c '{include:.}')
echo "test_matrix=$MATRIX" >> "$GITHUB_OUTPUT"
echo "Matrix: $(echo "$INCLUDES" | jq 'length') modules"
echo "$MATRIX" | jq .
- name: Extract tool versions
run: |
echo "SCARB_VERSION=$(grep '^scarb ' .tool-versions | awk '{print $2}')" >> "$GITHUB_ENV"
echo "FOUNDRY_VERSION=$(grep '^starknet-foundry ' .tool-versions | awk '{print $2}')" >> "$GITHUB_ENV"
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: ${{ env.SCARB_VERSION }}
cache: false
- uses: foundry-rs/setup-snfoundry@v4
with:
starknet-foundry-version: ${{ env.FOUNDRY_VERSION }}
- name: Cache Scarb dependencies
uses: actions/cache@v4
with:
path: ~/.cache/scarb
key: ${{ runner.os }}-scarb-deps-${{ hashFiles('**/Scarb.toml', '**/Scarb.lock') }}
restore-keys: |
${{ runner.os }}-scarb-deps-
- name: Cache Cargo (snforge plugin + Scarb Rust deps)
uses: actions/cache@v4
with:
path: ~/.cargo
key: ${{ runner.os }}-cargo-snforge-${{ env.FOUNDRY_VERSION }}
restore-keys: |
${{ runner.os }}-cargo-snforge-
- name: Fetch dependencies
run: scarb fetch
- name: Warm snforge plugin cache
run: snforge --version
- name: Verify workspace compiles (no warnings)
run: |
scarb build --workspace 2>&1 | tee /tmp/build.log
if grep -qE '^ --> .*\.cairo:[0-9]+:[0-9]+' /tmp/build.log; then
echo "::error::Build produced compiler warnings. Fix all warnings before merging."
grep -B1 -E '^ --> .*\.cairo:[0-9]+:[0-9]+' /tmp/build.log
exit 1
fi
test:
name: test (${{ matrix.module }})
needs: [changes, setup]
if: needs.changes.outputs.has_tests == 'true'
runs-on: ${{ matrix.runner }}
timeout-minutes: 10
strategy:
fail-fast: true
matrix: ${{ fromJson(needs.setup.outputs.test_matrix) }}
steps:
- uses: actions/checkout@v6
- name: Extract tool versions
run: |
echo "SCARB_VERSION=$(grep '^scarb ' .tool-versions | awk '{print $2}')" >> "$GITHUB_ENV"
echo "FOUNDRY_VERSION=$(grep '^starknet-foundry ' .tool-versions | awk '{print $2}')" >> "$GITHUB_ENV"
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: ${{ env.SCARB_VERSION }}
cache: false
- uses: foundry-rs/setup-snfoundry@v4
with:
starknet-foundry-version: ${{ env.FOUNDRY_VERSION }}
- name: Cache Scarb dependencies
uses: actions/cache@v4
with:
path: ~/.cache/scarb
key: ${{ runner.os }}-scarb-deps-${{ hashFiles('**/Scarb.toml', '**/Scarb.lock') }}
restore-keys: |
${{ runner.os }}-scarb-deps-
- name: Cache Cargo (snforge plugin)
uses: actions/cache@v4
with:
path: ~/.cargo
key: ${{ runner.os }}-cargo-snforge-${{ env.FOUNDRY_VERSION }}
restore-keys: |
${{ runner.os }}-cargo-snforge-
- name: Cache tools
uses: actions/cache@v4
id: cache-tools
with:
path: |
~/.local/bin/cairo-coverage
~/.local/bin/universal-sierra-compiler
key: ${{ runner.os }}-tools-cairo-coverage-universal-sierra-compiler-v1
- name: Install tools
if: steps.cache-tools.outputs.cache-hit != 'true'
run: |
(curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh) &
(curl -L https://raw.githubusercontent.com/software-mansion/universal-sierra-compiler/master/scripts/install.sh | sh) &
wait
- name: Cache compiled packages
uses: actions/cache@v4
with:
path: target/
key: ${{ runner.os }}-target-${{ matrix.package }}-${{ matrix.module }}-${{ env.SCARB_VERSION }}-${{ hashFiles('packages/**/*.cairo', '**/Scarb.toml', '**/Scarb.lock') }}
restore-keys: |
${{ runner.os }}-target-${{ matrix.package }}-${{ matrix.module }}-${{ env.SCARB_VERSION }}-
- name: Run tests with coverage
run: |
if [ "${{ matrix.package }}" = "game_components_${{ matrix.module }}" ]; then
snforge test -p ${{ matrix.package }} --fuzzer-runs ${{ matrix.fuzzer_runs }} --coverage 2>&1 | tee /tmp/test.log
else
snforge test -p ${{ matrix.package }} "::${{ matrix.module }}::" --fuzzer-runs ${{ matrix.fuzzer_runs }} --coverage 2>&1 | tee /tmp/test.log
fi
- name: Check for compiler warnings
if: always() && steps.cache-tools.outcome != 'failure'
run: |
if [ -f /tmp/test.log ] && grep -qE '^ --> .*\.cairo:[0-9]+:[0-9]+' /tmp/test.log; then
echo "::error::Test compilation produced warnings. Fix all warnings before merging."
grep -B1 -E '^ --> .*\.cairo:[0-9]+:[0-9]+' /tmp/test.log
exit 1
fi
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: ./coverage.lcov
flags: ${{ matrix.module }}
token: ${{ secrets.CODECOV_TOKEN }}
# AI code review (Claude + Codex) is delegated to the org-shared reusable
# workflow so the review logic and model config live in one place. This repo
# supplies only .github/review-agents.json + .github/prompts/*.md.
#
# Deliberately has no `needs:` — a skipped dependency skips its dependents,
# and `lint` is conditional on packages/ changing, so gating on it would
# silently drop reviews on docs-only PRs.
review:
uses: Provable-Games/.github/.github/workflows/code-review.yml@v1
with:
# Preserves the merge gate the inline review jobs had: a [CRITICAL] or
# [HIGH] finding fails the job rather than only posting a comment.
block_on_severity: true
# Repo default workflow token is read-only; a reusable workflow's job
# permissions can't exceed the caller's, so grant the write scopes the
# review jobs need (PR comments + Claude OIDC) here.
#
# @v1 is a mutable tag on purpose — it is the org convention every caller
# uses, and it is what lets a model deprecation be fixed once upstream
# rather than in seven repos. Pinning a SHA here would defeat the reason
# this workflow is shared. `secrets: inherit` likewise: the callee is
# first-party and declares exactly CLAUDE_CODE_OAUTH_TOKEN and
# CODEX_AUTH_DOT_JSON, and inheriting means a new upstream secret does not
# need a PR in every caller.
permissions:
contents: read
issues: write
pull-requests: write
id-token: write
secrets: inherit
pr-ci:
needs:
- changes
- infra-validate
- lint
- setup
- test
- review
if: always()
runs-on: ubuntu-latest
steps:
- name: Evaluate results
env:
NEEDS_JSON: ${{ toJson(needs) }}
run: |
FAILED=$(echo "$NEEDS_JSON" | jq -r '
to_entries[]
| select(.value.result == "failure" or .value.result == "cancelled")
| .key
')
if [ -n "$FAILED" ]; then
echo "::error::Failing jobs:"
echo "$FAILED"
exit 1
fi
echo "All required jobs passed or were skipped"