Skip to content

fix crash, add cover section. #878

fix crash, add cover section.

fix crash, add cover section. #878

name: Build and publish Docker image (GHCR)
on:
push:
branches: [main]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: write
packages: write
id-token: write
env:
IMAGE: ghcr.io/${{ github.repository }}
ELIXIR_VERSION: "1.20.1"
OTP_VERSION: "29.0.2"
jobs:
build:
name: Build and publish (${{ matrix.adapter }})
runs-on: ubuntu-latest
strategy:
matrix:
include:
- adapter: sqlite
tags_suffix: ""
extra_tag_suffix: ""
- adapter: postgres
tags_suffix: "-postgres"
extra_tag_suffix: "-postgres"
steps:
- name: Checkout repository (full history for commit count)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Compute version
id: version
run: |
git config --global --add safe.directory $GITHUB_WORKSPACE || true
git fetch --prune --unshallow || true
COMMIT_COUNT=$(git rev-list --count HEAD)
VERSION="1.0.${COMMIT_COUNT}"
echo "Generated version=${VERSION}"
echo "APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "GAMEND_CONTENT_APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "version=${VERSION}" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and (conditionally) push multi-arch image
uses: docker/build-push-action@v4
with:
context: .
file: Dockerfile
build-args: |
GAMEND_CONTENT_APP_VERSION=${{ env.APP_VERSION }}
GAMEND_DB_ADAPTER=${{ matrix.adapter }}
platforms: linux/amd64,linux/arm64
# amd64 reuses the smoke build's layers; arm64 keeps its own scope.
# Without this every run rebuilt the Rust toolchain, every dependency
# and every NIF from scratch.
cache-from: type=gha,scope=${{ matrix.adapter }}
cache-to: type=gha,mode=max,scope=${{ matrix.adapter }}
push: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
tags: |
${{ env.IMAGE }}:latest${{ matrix.tags_suffix }}
${{ env.IMAGE }}:${{ github.sha }}${{ matrix.extra_tag_suffix }}
smoke:
name: Boot the image (${{ matrix.adapter }})
runs-on: ubuntu-latest
# Deliberately independent of `build`: this proves the image starts and
# serves traffic, which unit tests cannot. config/runtime.exs is evaluated
# at container start, not at build time, so a green build says nothing
# about whether the app boots. It does not gate publishing.
strategy:
fail-fast: false
matrix:
adapter: [sqlite, postgres]
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v2
- name: Build the image (amd64, loaded locally)
uses: docker/build-push-action@v4
with:
context: .
file: Dockerfile
build-args: |
GAMEND_DB_ADAPTER=${{ matrix.adapter }}
platforms: linux/amd64
load: true
tags: gamend-smoke:${{ matrix.adapter }}
cache-from: type=gha,scope=${{ matrix.adapter }}
cache-to: type=gha,mode=max,scope=${{ matrix.adapter }}
- name: Boot it and check it serves traffic
run: |
set -euo pipefail
docker network create smoke-net
# The postgres container reads its own POSTGRES_* names to
# initialise; the server reads ours. They are deliberately distinct.
{
echo "GAMEND_AUTH_SECRET_KEY_BASE=$(head -c 48 /dev/urandom | base64 | tr -d '\n')"
echo "GAMEND_HTTP_HOST=localhost"
echo "GAMEND_OBSERVABILITY_LOG_LEVEL=info"
} > smoke.env
if [ "${{ matrix.adapter }}" = "postgres" ]; then
docker run -d --name smoke-db --network smoke-net \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_USER=postgres \
-e POSTGRES_DB=gamend_prod \
postgres:15
for _ in $(seq 1 30); do
docker exec smoke-db pg_isready -U postgres >/dev/null 2>&1 && break
sleep 2
done
{
echo "GAMEND_DB_POSTGRES_HOST=smoke-db"
echo "GAMEND_DB_POSTGRES_USER=postgres"
echo "GAMEND_DB_POSTGRES_PASSWORD=postgres"
echo "GAMEND_DB_POSTGRES_DB=gamend_prod"
} >> smoke.env
fi
docker run -d --name gamend-smoke --network smoke-net \
-p 4000:4000 --env-file smoke.env \
gamend-smoke:${{ matrix.adapter }}
for _ in $(seq 1 60); do
if curl -fsS http://localhost:4000/api/v1/health | grep -q '"status":"ok"'; then
echo "healthy"
exit 0
fi
if [ -z "$(docker ps -q -f name=gamend-smoke)" ]; then
echo "::error::container exited before serving traffic"
docker logs gamend-smoke
exit 1
fi
sleep 2
done
echo "::error::timed out waiting for /api/v1/health"
docker logs gamend-smoke
exit 1
# The build arg has to reach the compiled code, not just the shell: an
# ignored GAMEND_DB_ADAPTER once published a SQLite build as -postgres.
- name: Verify the image compiled against the expected adapter
run: |
set -euo pipefail
adapter=$(docker exec gamend-smoke sh -c \
'mix run --no-start -e "IO.puts(inspect(Gamend.Repo.__adapter__()))"' | tail -1)
echo "compiled adapter: $adapter"
case "${{ matrix.adapter }}" in
postgres) echo "$adapter" | grep -q "Ecto.Adapters.Postgres" ;;
sqlite) echo "$adapter" | grep -q "Ecto.Adapters.SQLite3" ;;
esac
- name: Teardown
if: always()
run: |
docker logs gamend-smoke 2>&1 | tail -50 || true
docker rm -f gamend-smoke smoke-db 2>/dev/null || true
docker network rm smoke-net 2>/dev/null || true
lint:
name: Elixir lint
runs-on: ubuntu-latest
container:
image: elixir:1.20.1-otp-29
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install system dependencies
run: |
apt update
apt install -y build-essential erlang-dev curl
- name: Install Rust toolchain (for ex_sctp WebRTC DataChannels)
run: |
curl https://sh.rustup.rs -sSf | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
- name: Install dependencies
run: MIX_ENV=dev mix deps.get
- name: Install web dependencies
working-directory: apps/gamend_web
run: MIX_ENV=dev mix deps.get
- name: Check code formatting
run: MIX_ENV=dev mix format --check-formatted
- name: Credo (strict)
run: MIX_ENV=dev mix credo --strict
- name: API conventions (docs/specs/api-conventions.md)
run: MIX_ENV=dev mix gamend.api.lint
- name: Credo (strict, web app)
working-directory: apps/gamend_web
run: MIX_ENV=dev mix credo --strict
- name: Settings docs are in sync with the declarations
run: |
MIX_ENV=dev mix gamend.settings.env_example --check
MIX_ENV=dev mix gamend.settings.guide --check
MIX_ENV=dev mix gamend.theme.extract --check
- name: Audit dependencies for known vulnerabilities
run: MIX_ENV=dev mix deps.audit
dialyzer:
name: Dialyzer
runs-on: ubuntu-latest
container:
image: elixir:1.20.1-otp-29
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install system dependencies
run: |
apt update
apt install -y build-essential erlang-dev curl
- name: Install Rust toolchain (for ex_sctp WebRTC DataChannels)
run: |
curl https://sh.rustup.rs -sSf | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
# Each app is analysed as a standalone project below, and a standalone
# project resolves its own deps/ — the root fetch does not reach them.
- name: Install dependencies
run: MIX_ENV=dev mix deps.get
- name: Install dependencies (gamend_core)
working-directory: apps/gamend_core
run: MIX_ENV=dev mix deps.get
- name: Install dependencies (gamend_web)
working-directory: apps/gamend_web
run: MIX_ENV=dev mix deps.get
# The umbrella apps are path deps, so they land in the *deps* PLT. Its
# cache key is mix.lock, which does not change when app source does — so
# the .plt.hash is deliberately not cached: restoring it would make
# dialyxir report "PLT is up to date" and skip revalidation, and every
# module added since the cache was written would look like it does not
# exist. Without the hash it revalidates and updates the PLT in place.
#
# Each standalone app build keeps its own PLT, so all three are cached.
- name: Cache PLTs
uses: actions/cache@v4
with:
path: |
_build/dev/*.plt
apps/gamend_core/_build/dev/*.plt
apps/gamend_web/_build/dev/*.plt
key: plt-${{ runner.os }}-${{ hashFiles('mix.lock') }}
restore-keys: |
plt-${{ runner.os }}-
# The root project is a thin wrapper: analysing only it covers ~11
# modules. The domain code lives in the two umbrella apps, so each must
# be analysed in its own right or the job is green by vacuity.
- name: Run dialyzer
run: MIX_ENV=dev mix dialyzer --format github
- name: Run dialyzer (gamend_core)
working-directory: apps/gamend_core
run: MIX_ENV=dev mix dialyzer --format github
- name: Run dialyzer (gamend_web)
working-directory: apps/gamend_web
run: MIX_ENV=dev mix dialyzer --format github
check-outdated:
name: Check for outdated dependencies
runs-on: ubuntu-latest
container:
image: elixir:1.20.1-otp-29
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
- name: Install dependencies
run: mix deps.get
- name: Check for outdated dependencies
run: |
echo "Checking for outdated dependencies..."
# Capture output to a file to avoid failing the step if `mix hex.outdated` returns non-zero
OUT_FILE=outdated.txt
mix hex.outdated > "$OUT_FILE" 2>&1 || true
cat "$OUT_FILE"
# Fail only if there are actionable updates ("Update possible"). Warn if there are "Update not possible" entries.
if grep -q "Update possible" "$OUT_FILE"; then
echo "❌ FAILURE: Some dependencies have available updates. Please update and test thoroughly before merging."
# Run mix deps.update --all
# mix deps.get
exit 1
else
if grep -q "Update not possible" "$OUT_FILE"; then
echo "⚠️ Some dependencies have major-version updates that are blocked by constraints (Update not possible). Please plan major upgrades separately."
else
echo "✅ All dependencies are up to date or have compatible updates."
fi
fi
# Add to GitHub step summary for better visibility
echo "## Outdated Dependencies Report" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
cat "$OUT_FILE" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
test-sqlite:
name: Test with SQLite
runs-on: ubuntu-latest
container:
image: elixir:1.20.1-otp-29
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install system dependencies
run: |
apt update
apt install -y build-essential erlang-dev curl
- name: Install Rust toolchain (for ex_sctp WebRTC DataChannels)
run: |
curl https://sh.rustup.rs -sSf | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
- name: Install dependencies
run: MIX_ENV=test mix deps.get
- name: Install web test dependencies
working-directory: apps/gamend_web
run: MIX_ENV=test mix deps.get
- name: Compile
run: MIX_ENV=test mix compile --warnings-as-errors
- name: Run tests
run: MIX_ENV=test mix test
test-postgres:
name: Test with PostgreSQL
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: gamend_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
container:
image: elixir:1.20.1-otp-29
# config/test.exs switches to Postgres on GAMEND_DB_POSTGRES_*; the old
# POSTGRES_* names matched nothing, so this job silently tested SQLite.
env:
GAMEND_DB_POSTGRES_HOST: postgres
GAMEND_DB_POSTGRES_USER: postgres
GAMEND_DB_POSTGRES_PASSWORD: postgres
GAMEND_DB_POSTGRES_DB: gamend_test
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install system dependencies
run: |
apt update
apt install -y build-essential erlang-dev curl
- name: Install Rust toolchain (for ex_sctp WebRTC DataChannels)
run: |
curl https://sh.rustup.rs -sSf | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
- name: Install dependencies
run: MIX_ENV=test mix deps.get
- name: Install web test dependencies
working-directory: apps/gamend_web
run: MIX_ENV=test mix deps.get
- name: Compile
run: MIX_ENV=test mix compile --warnings-as-errors
- name: Run tests
run: MIX_ENV=test mix test
commands:
name: Mix commands (setup, db.reset)
runs-on: ubuntu-latest
container:
image: elixir:1.20.1-otp-29
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install system dependencies
run: |
apt update
apt install -y build-essential erlang-dev curl
- name: Install Rust toolchain (for ex_sctp WebRTC DataChannels)
run: |
curl https://sh.rustup.rs -sSf | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
# Exercise the documented developer commands end-to-end (SQLite dev
# database), so alias/task breakage is caught even though tests bypass
# these aliases.
- name: mix setup
run: mix setup
- name: mix db.reset
run: mix db.reset
publish-js-sdk:
name: Publish JS SDK
# Disable it temporarily
if: false
#if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout repository (full history for commit count)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Compute version
id: version
run: |
git config --global --add safe.directory $GITHUB_WORKSPACE || true
git fetch --prune --unshallow || true
COMMIT_COUNT=$(git rev-list --count HEAD)
# Build a simple semver style version used on CI: 1.0.<commit_count>
VERSION="1.0.${COMMIT_COUNT}"
echo "Generated version=${VERSION}"
# Export for remaining steps in this job
echo "APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "GAMEND_CONTENT_APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "version=${VERSION}" >> $GITHUB_OUTPUT
- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ env.ELIXIR_VERSION }}
otp-version: ${{ env.OTP_VERSION }}
install-hex: false
install-rebar: false
- name: Install Java (for openapi generator)
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
cache: 'npm'
# repository uses JS packages under the `clients` subfolder. When caching
# npm dependencies we need to point setup-node at the lockfile location
# so the cache key is computed correctly.
cache-dependency-path: clients/package-lock.json
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
- name: Install Elixir deps (for generating spec)
run: |
mix deps.get
- name: Prepare database (app boot requires a migrated DB)
run: |
mix ecto.create --quiet -r Gamend.Repo
mix db.migrate
- name: Regenerate OpenAPI JSON
run: |
mix openapi.spec.json --spec GamendWeb.ApiSpec --filename clients/javascript/openapi.json --pretty=true
- name: Generate JS client artifacts
run: |
cd clients
npm ci
npm run generate
- name: Ensure license is included for JS SDK
run: |
cp LICENSE clients/javascript/LICENSE
jq '.license = "MIT"' clients/javascript/package.json > clients/javascript/package.json.tmp && mv clients/javascript/package.json.tmp clients/javascript/package.json
- name: Update package.json with repository field (https://github.com/appsinacup/gamend.git)
run: |
REPO_FIELD='"repository": { "type": "git", "url": "git+https://github.com/appsinacup/gamend.git" },'
jq ". + { $REPO_FIELD }" clients/javascript/package.json > clients/javascript/package.json.tmp && mv clients/javascript/package.json.tmp clients/javascript/package.json
- name: Add phoenix as a dependency
run: |
jq '.dependencies.phoenix = ">=1.7.0"' \
clients/javascript/package.json > clients/javascript/package.json.tmp \
&& mv clients/javascript/package.json.tmp clients/javascript/package.json
- name: Apply version and publish
run: |
cd clients/javascript
echo "Publishing package with version=${{ steps.version.outputs.version }}"
npm version ${{ steps.version.outputs.version }} --no-git-tag-version
# Trusted Publishing (OIDC) — npm 11+ auto-detects the OIDC
# environment and authenticates without tokens. Provenance
# attestations are generated automatically.
npm publish --access public
build-godot-sdk:
runs-on: ubuntu-latest
name: Generate Godot client and upload addons
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Compute version
id: version
run: |
git config --global --add safe.directory $GITHUB_WORKSPACE || true
git fetch --prune --unshallow || true
COMMIT_COUNT=$(git rev-list --count HEAD)
# Build a simple semver style version used on CI: 1.0.<commit_count>
VERSION="1.0.${COMMIT_COUNT}"
echo "Generated version=${VERSION}"
# Export for remaining steps in this job
echo "APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "GAMEND_CONTENT_APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "version=${VERSION}" >> $GITHUB_OUTPUT
- name: Set up Elixir (for mix openapi task)
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ env.ELIXIR_VERSION }}
otp-version: ${{ env.OTP_VERSION }}
install-hex: false
install-rebar: false
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
- name: Install dependencies (mix deps.get)
working-directory: .
run: |
mix deps.get
- name: Prepare database (app boot requires a migrated DB)
run: |
mix ecto.create --quiet -r Gamend.Repo
mix db.migrate
- name: Run Godot generation script
env:
APP_VERSION: ${{ env.APP_VERSION }}
run: |
echo "Generating Godot client with APP_VERSION=${APP_VERSION:-$(echo $APP_VERSION)}"
cd clients
./generate_godot.sh
- name: Upload addons zip
uses: actions/upload-artifact@v4
with:
name: godot_addons
path: godot_addons
- name: Archive
shell: sh
run: |
zip -r "godot_addons.zip" godot_addons
- name: Release
if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
uses: softprops/action-gh-release@v2
with:
tag_name: latest
name: Gamend Godot SDK Nightly
files: |
godot_addons.zip
generate_release_notes: false
make_latest: true
fail_on_unmatched_files: true
publish-hex:
name: Publish Hex packages
if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
needs: [test-sqlite]
runs-on: ubuntu-latest
env:
HEX_API_KEY: ${{ secrets.HEX_KEY }}
steps:
- name: Checkout repository (full history for commit count)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Compute version
id: version
run: |
git config --global --add safe.directory $GITHUB_WORKSPACE || true
git fetch --prune --unshallow || true
COMMIT_COUNT=$(git rev-list --count HEAD)
VERSION="1.0.${COMMIT_COUNT}"
echo "Generated version=${VERSION}"
echo "APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "GAMEND_CONTENT_APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "version=${VERSION}" >> $GITHUB_OUTPUT
- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{ env.OTP_VERSION }}
elixir-version: ${{ env.ELIXIR_VERSION }}
install-hex: false
install-rebar: false
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
- name: Publish gamend_sdk
working-directory: sdk
run: |
mix deps.get
# Do not swallow the exit code: publishing silently failed for
# months behind an `|| echo`. A version that already exists is
# not an error to hex.publish, so a non-zero exit is real.
mix hex.publish --yes
- name: Publish gamend_plugin_tools
working-directory: sdk_tools
run: |
mix deps.get
# Do not swallow the exit code: publishing silently failed for
# months behind an `|| echo`. A version that already exists is
# not an error to hex.publish, so a non-zero exit is real.
mix hex.publish --yes
# gamend_core and gamend_web are NOT published to Hex.
#
# gamend_core depends on pigeon by git ref: upstream merged the
# kadabra->mint rewrite in May 2025 and bumped mix.exs to 2.1.0 but never
# released it (codedge-llc/pigeon "Publish v2.1.0"), so the newest Hex
# release is still 2.0.1. Hex refuses a package with any non-Hex dep, and
# 2.0.1 would drag back httpoison -> hackney and reintroduce the
# vulnerability chain that issue is about. gamend_web follows: its
# published form points at {:gamend_core, "~> 1.0"}, which would not exist.
#
# Consumers use the GitHub sparse deps instead (see gamend_starter), and
# the API reference is published to docs.gamend.org by the `docs` job — so
# nothing downstream depends on these being on Hex. Re-enable the block
# below if pigeon 2.1.0 ships.
#
# - name: Rewrite in_umbrella deps and stamp version for core/web
# run: |
# # gamend_web depends on gamend_core by path; Hex only accepts Hex
# # deps, so the published package points at the version being cut.
# # This matched `in_umbrella: true` for months, which the file never
# # said — so the substitution silently did nothing and every
# # gamend_web publish failed.
# sed -i "s|{:gamend_core, path: \"../gamend_core\"}|{:gamend_core, \"~> 1.0\"}|g" \
# apps/gamend_web/mix.exs
# grep -q '{:gamend_core, "~> 1.0"}' apps/gamend_web/mix.exs ||
# { echo "::error::gamend_core path dep rewrite failed - gamend_web would publish broken"; exit 1; }
# # Remove heroicons GitHub dep for Hex packaging (Hex only allows Hex deps).
# # Host/consumer apps must provide icon CSS generation for `hero-*` classes.
# sed -i '/{:heroicons/,/depth: 1}/d' apps/gamend_web/mix.exs
# sed -i "s|@version \"[^\"]*\"|@version \"${APP_VERSION}\"|g" \
# apps/gamend_core/mix.exs \
# apps/gamend_web/mix.exs
#
# - name: Publish gamend_core
# working-directory: apps/gamend_core
# run: |
# mix deps.get
# # Do not swallow the exit code: publishing silently failed for
# # months behind an `|| echo`. A version that already exists is
# # not an error to hex.publish, so a non-zero exit is real.
# mix hex.publish --yes
#
# - name: Publish gamend_web
# working-directory: apps/gamend_web
# run: |
# mix deps.get
# # Do not swallow the exit code: publishing silently failed for
# # months behind an `|| echo`. A version that already exists is
# # not an error to hex.publish, so a non-zero exit is real.
# mix hex.publish --yes
docs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Compute version
id: version
run: |
git config --global --add safe.directory $GITHUB_WORKSPACE || true
git fetch --prune --unshallow || true
COMMIT_COUNT=$(git rev-list --count HEAD)
# Build a simple semver style version used on CI: 1.0.<commit_count>
VERSION="1.0.${COMMIT_COUNT}"
echo "Generated version=${VERSION}"
# Export for remaining steps in this job
echo "APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "GAMEND_CONTENT_APP_VERSION=${VERSION}" >> $GITHUB_ENV
echo "version=${VERSION}" >> $GITHUB_OUTPUT
- name: Setup Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ env.ELIXIR_VERSION }}
otp-version: ${{ env.OTP_VERSION }}
install-hex: false
install-rebar: false
- name: Set up Elixir helpers
working-directory: /tmp
run: |
mix local.hex --force
mix local.rebar --force
- name: Install dependencies
working-directory: apps/gamend_core
run: |
mix deps.get
# Docs are generated from gamend_core: it holds the public API
# (Gamend.* domain modules, hooks). The root :gamend_host is a
# thin wrapper, so building docs there yields an almost-empty reference.
# Output to the repo-root ./doc so the publish step below finds it.
- name: Generate docs
working-directory: apps/gamend_core
env:
APP_VERSION: ${{ env.APP_VERSION }}
run: |
echo "Using APP_VERSION=${APP_VERSION:-$(echo $APP_VERSION)}"
MIX_ENV=dev mix docs -o ../../doc
- name: Publish to gh-pages
if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./doc
# keep history to allow diffs in gh-pages
publish_branch: gh-pages
# GitHub wrote CNAME into gh-pages when the custom domain was set, but
# ExDoc's output does not contain one — publishing without this would
# delete it on the next push and take docs.gamend.org down.
cname: docs.gamend.org