name: ci
on:
push:
branches: [master]
tags: ["v*"]
pull_request:
# A newer push supersedes this run: on a branch or PR the run in
# progress is cancelled; on master it finishes, and only a run still
# waiting is replaced.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
test:
# Self-hosted ARC runners (same as ../angler). Swap for `ubuntu-latest`
# if you'd rather use GitHub-hosted runners. The Postgres service container
# requires the runner to support service containers (Docker/dind).
runs-on: arc-runner-set
env:
MIX_ENV: test
# Under forgejo-runner/act the job runs in a container, so the postgres
# service is reached by its service name, not localhost.
POSTGRES_HOST: postgres
# service is reached at its container port under forgejo-runner
POSTGRES_PORT: 5432
# Cap the test DB pool below the Postgres service's default
# max_connections (100); big CI runners otherwise scale pool to cores×2
# and exhaust connections.
POOL_SIZE: "40"
services:
postgres:
image: docker.io/postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
# config/test.exs connects on 54329 (matches compose.yml), so publish
# the service container's 5432 on that host port.
ports:
- 54329:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 3s
--health-retries 5
steps:
- uses: actions/checkout@v4
# ARC's default runner image is bare; install the toolchain needed to
# compile native deps.
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends make build-essential
# Forge fork of erlef/setup-beam: defaults ImageOS to ubuntu-24.04,
# since forgejo/act injects an unmappable ImageOS from the runs-on label.
- uses: https://gg.neiam.co/neiam/action-setup-beam@v1
with:
# Match the build image in the Dockerfile (OTP >= 27.3.3; 27.2 breaks
# `mix local.hex` against hex.pm's current TLS chain).
elixir-version: "1.20.4"
otp-version: "28.5.0.8"
- uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: ${{ runner.os }}-mix-
- run: mix deps.get
# `mix precommit` = compile --warnings-as-errors, deps.unlock --unused,
# format, test (which creates + migrates the test DB).
- run: mix precommit
# The public registries from GitHub only. The forge reads
# .github/workflows too, and there .forgejo/workflows/image.yml
# publishes to the forge's own registry instead.
image:
if: github.server_url == 'https://github.com'
needs: test
runs-on: arc-runner-set
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
# GHCR uses a stored token (GHCR_TOKEN). Docker Hub and Quay creds come from
# org-wide secrets (DOCKERHUB_USER/DOCKERHUB_TOKEN, QUAY_USERNAME/QUAY_TOKEN).
- name: Log in to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ vars.GHCR_USER || github.actor }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: docker.io
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to Quay
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_TOKEN }}
- id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/${{ github.repository }}
docker.io/neiam/diogramos
quay.io/neiam/diogramos
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
type=raw,value=latest,enable={{is_default_branch}}
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# The Android client, published to the forge's own F-Droid repository.
#
# Needs two secrets, and skips cleanly without them rather than failing
# a build for a package most forks will not publish:
#
# ANDROID_KEYSTORE base64 of the release keystore (.jks)
# ANDROID_KEYSTORE_PASS its password
#
# Generate one once and keep it. Android checks the signing certificate
# on every update, so losing it means every install has to be
# uninstalled and redone by hand -- there is no rotation:
#
# keytool -genkeypair -v -keystore release.jks -storetype PKCS12 \
# -alias diogramos -keyalg RSA -keysize 4096 -validity 10000 \
# -dname "CN=Diogramos, O=neiam, C=US"
# base64 -w0 release.jks
#
# This is the APK signing key, not the F-Droid index key -- that one is
# per-owner, lives in OpenBao, and never comes near CI.
android:
needs: test
runs-on: arc-runner-set
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Resolve the forge URL
env:
CI_FORGE_URL: ${{ vars.CI_FORGE_URL }}
run: |
set -euo pipefail
forge_url=${CI_FORGE_URL:-${GITHUB_SERVER_URL:-}}
if [ -z "$forge_url" ]; then
echo "No forge URL available; set a CI_FORGE_URL variable." >&2
exit 1
fi
echo "FORGE_URL=$forge_url" >> "$GITHUB_ENV"
# A secret cannot be read in a job-level `if`, so the gate is a step
# output that everything after it checks.
- name: Unpack the signing keystore
id: keystore
env:
KEYSTORE: ${{ secrets.ANDROID_KEYSTORE }}
KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
run: |
set -euo pipefail
if [ -z "${KEYSTORE:-}" ] || [ -z "${KEYSTORE_PASS:-}" ]; then
echo "No ANDROID_KEYSTORE / ANDROID_KEYSTORE_PASS set -- skipping."
echo "An unsigned or debug-signed APK is worse than none: the debug"
echo "key is shared by every developer with an SDK, so publishing"
echo "one would let anyone ship an 'update' to it."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
umask 077
printf '%s' "$KEYSTORE" | base64 -d > release.jks
printf '%s' "$KEYSTORE_PASS" > keystore.pass
echo "skip=false" >> "$GITHUB_OUTPUT"
- name: Build and sign the APK
if: steps.keystore.outputs.skip != 'true'
env:
RUN_NUMBER: ${{ github.run_number }}
run: |
set -euo pipefail
# One stamp for the whole run, computed here rather than inside
# Gradle, so every artifact of a run agrees on its version even
# if the build is retried a step at a time.
stamp=$(date -u +%s)
echo "version: 0.1.0-dev-$stamp (versionCode $RUN_NUMBER)"
docker buildx build \
--target android-export \
--build-arg GITHUB_RUN_NUMBER="$RUN_NUMBER" \
--build-arg SOURCE_DATE_EPOCH="$stamp" \
--secret id=keystore,src=./release.jks \
--secret id=keystore_password,src=./keystore.pass \
--output type=local,dest=./apk \
--file android/Dockerfile android
# Gone before anything else runs, so a later step or a failure
# mid-job cannot leave it on a persistent runner.
- name: Shred the keystore
if: always()
run: shred -u release.jks keystore.pass 2>/dev/null || rm -f release.jks keystore.pass
- name: Publish to the F-Droid repository
if: steps.keystore.outputs.skip != 'true'
env:
REPOSITORY: ${{ github.repository }}
TOKEN: ${{ github.token }}
run: |
set -euo pipefail
curl --fail-with-body -sS \
-u "diogramos-actions:$TOKEN" \
-H "Content-Type: application/vnd.android.package-archive" \
--data-binary "@apk/app-release.apk" \
"$FORGE_URL/pkg/fdroid/$REPOSITORY/upload"
echo
- name: How to install it
if: steps.keystore.outputs.skip != 'true'
env:
OWNER: ${{ github.repository_owner }}
run: |
set -euo pipefail
fingerprint=$(curl -fsSL "$FORGE_URL/pkg/_keys/fdroid/$OWNER")
cat <<EOF
Add this repository in F-Droid (Settings -> Repositories -> +):
$FORGE_URL/pkg/fdroid/$OWNER/_all?fingerprint=$fingerprint
One entry covers every Android app $OWNER publishes. The
fingerprint pins the index signing certificate; F-Droid refuses
an index signed by anything else.
EOF
neiam
/diogramos
diogramos
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
9.3 KiB · text
History
dc2edab