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
92 changes: 58 additions & 34 deletions .github/actions/setup-godot/action.yml
Original file line number Diff line number Diff line change
@@ -1,55 +1,79 @@
name: 'Setup Godot'
description: 'Install Godot binary with caching and optionally install export templates'
name: Setup Godot
description: Install a checksum-verified Godot Linux binary
inputs:
godot-version:
description: 'Godot version to install (e.g., 4.4.1)'
description: Godot version to install
required: true
godot-linux-x86-64-sha256:
description: SHA-256 of the immutable Linux x86_64 release ZIP
required: true
install-templates:
description: 'Whether to install export templates'
required: false
default: 'false'
godot-templates-sha256:
required: false
default: ''

runs:
using: 'composite'
using: composite
steps:
- name: Cache Godot binary
id: cache-godot
- name: Cache verified Godot archive
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: /usr/local/bin/godot
key: godot-${{ inputs.godot-version }}-${{ runner.os }}
path: ~/.cache/aviorstudio/godot/${{ inputs.godot-version }}
key: godot-${{ inputs.godot-version }}-linux-x86-64-${{ inputs.godot-linux-x86-64-sha256 }}

- name: Install Godot
if: steps.cache-godot.outputs.cache-hit != 'true'
- name: Download, verify, and install Godot
shell: bash
env:
GODOT_VERSION: ${{ inputs.godot-version }}
GODOT_SHA256: ${{ inputs.godot-linux-x86-64-sha256 }}
run: |
wget -q https://github.com/godotengine/godot/releases/download/${{ inputs.godot-version }}-stable/Godot_v${{ inputs.godot-version }}-stable_linux.x86_64.zip
unzip -q Godot_v${{ inputs.godot-version }}-stable_linux.x86_64.zip
chmod +x Godot_v${{ inputs.godot-version }}-stable_linux.x86_64
sudo mv Godot_v${{ inputs.godot-version }}-stable_linux.x86_64 /usr/local/bin/godot
set -euo pipefail
install_dir="$HOME/.cache/aviorstudio/godot/$GODOT_VERSION"
archive="$install_dir/Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip"
binary="$install_dir/Godot_v${GODOT_VERSION}-stable_linux.x86_64"
mkdir -p "$install_dir"
if [ ! -f "$archive" ]; then
curl --fail --location --retry 3 --max-time 180 --output "$archive" \
"https://github.com/godotengine/godot-builds/releases/download/${GODOT_VERSION}-stable/$(basename "$archive")"
fi
printf '%s %s\n' "$GODOT_SHA256" "$archive" | sha256sum --check --strict
if [ ! -x "$binary" ]; then
unzip -q -o "$archive" -d "$install_dir"
chmod +x "$binary"
fi
ln -sf "$binary" "$install_dir/godot"
echo "$install_dir" >> "$GITHUB_PATH"

- name: Verify Godot installation
shell: bash
run: godot --version

- name: Cache Godot export templates
if: inputs.install-templates == 'true'
id: cache-godot-templates
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.local/share/godot/export_templates/${{ inputs.godot-version }}.stable
key: godot-templates-${{ inputs.godot-version }}-${{ runner.os }}

- name: Install Godot export templates
if: inputs.install-templates == 'true' && steps.cache-godot-templates.outputs.cache-hit != 'true'
shell: bash
run: |
mkdir -p ~/.local/share/godot/export_templates/${{ inputs.godot-version }}.stable
wget -q https://github.com/godotengine/godot/releases/download/${{ inputs.godot-version }}-stable/Godot_v${{ inputs.godot-version }}-stable_export_templates.tpz
unzip -q Godot_v${{ inputs.godot-version }}-stable_export_templates.tpz
mv templates/* ~/.local/share/godot/export_templates/${{ inputs.godot-version }}.stable/
set -euo pipefail
case "$(godot --version)" in
4.7.2.stable.*) godot --version ;;
*) echo 'Unexpected Godot version' >&2; exit 1 ;;
esac

- name: Verify Godot templates
- name: Install checksum-verified web templates
if: inputs.install-templates == 'true'
shell: bash
run: ls -la ~/.local/share/godot/export_templates/${{ inputs.godot-version }}.stable/
env:
GODOT_VERSION: ${{ inputs.godot-version }}
TEMPLATES_SHA256: ${{ inputs.godot-templates-sha256 }}
run: |
set -euo pipefail
test -n "$TEMPLATES_SHA256"
cache_dir="$HOME/.cache/aviorstudio/godot/$GODOT_VERSION"
archive="$cache_dir/Godot_v${GODOT_VERSION}-stable_export_templates.tpz"
target="$HOME/.local/share/godot/export_templates/${GODOT_VERSION}.stable"
if [ ! -f "$archive" ]; then
curl --fail --location --retry 3 --max-time 180 --output "$archive" \
"https://github.com/godotengine/godot-builds/releases/download/${GODOT_VERSION}-stable/$(basename "$archive")"
fi
printf '%s %s\n' "$TEMPLATES_SHA256" "$archive" | sha256sum --check --strict
temporary="$(mktemp -d)"
trap 'rm -rf "$temporary"' EXIT
unzip -q "$archive" 'templates/web_nothreads_release.zip' -d "$temporary"
mkdir -p "$target"
cp "$temporary/templates/web_nothreads_release.zip" "$target/"
27 changes: 14 additions & 13 deletions .github/actions/test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,12 @@ runs:
test -f addon/plugin.cfg

- name: Install verified Godot test binary
shell: bash
run: |
set -euo pipefail
archive="$RUNNER_TEMP/gd-env-godot.zip"
directory="$RUNNER_TEMP/gd-env-godot"
curl --fail --location --retry 3 --max-time 180 \
https://github.com/godotengine/godot-builds/releases/download/4.7.2-stable/Godot_v4.7.2-stable_linux.x86_64.zip \
--output "$archive"
printf '%s %s\n' '9aa00f7a605200940bce3027a567b782f49bd8e940dd06ae9e987bd65aee1b1467edd56ed84fcdcbdd44354bf613bdbb4e5d2913e925850368e150c59ed54c65' "$archive" | sha512sum --check
mkdir -p "$directory"
unzip -o "$archive" Godot_v4.7.2-stable_linux.x86_64 -d "$directory"
chmod +x "$directory/Godot_v4.7.2-stable_linux.x86_64"
echo "GODOT_BIN=$directory/Godot_v4.7.2-stable_linux.x86_64" >> "$GITHUB_ENV"
uses: ./.github/actions/setup-godot
with:
godot-version: '4.7.2'
godot-linux-x86-64-sha256: 'cadd3204e728a35d3f13adb7fd0d7902636b79f6b95c40c265eb73b6c35329e4'
install-templates: 'true'
godot-templates-sha256: 'f298490b8d44d934be425a5a65a51bf15f422428b229a06a6e11d9ffea248011'

# No `if: hashFiles(...)` guard. This step used to skip itself when
# tests/test.sh was absent, which is indistinguishable from the script being
Expand All @@ -50,3 +43,11 @@ runs:
- name: Godot tests
shell: bash
run: ./tests/test.sh

- name: Build and test exact release package
shell: bash
run: |
./scripts/package_addon.sh
./scripts/verify_package_checksum.sh
./tests/package_test.sh
./tests/web_package_test.sh
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ jobs:
# comment so the version is still readable. A tag is a moving reference:
# whoever can move it can run code in this job.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Test
uses: ./.github/actions/test

- name: Upload exact tested package evidence
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: tested-package-${{ github.event.pull_request.head.sha || github.sha }}
path: |
dist/@aviorstudio_gd-env.zip
dist/@aviorstudio_gd-env.zip.sha256
if-no-files-found: error
118 changes: 64 additions & 54 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,20 @@ on:
description: Version bump
required: true
type: choice
options:
- patch
- minor
- major
options: [patch, minor, major]

permissions:
contents: write
contents: read

concurrency: release-${{ github.repository }}

jobs:
release:
test:
runs-on: ubuntu-latest
# Bounded, so a step that hangs fails here rather than sitting until the
# runner's own timeout hours later.
timeout-minutes: 20
outputs:
version: ${{ steps.release.outputs.version }}
tag: ${{ steps.release.outputs.tag }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
Expand All @@ -34,72 +32,84 @@ jobs:
BUMP: ${{ inputs.bump }}
run: |
set -euo pipefail
if [ "$GITHUB_REF" != "refs/heads/main" ]; then
echo 'Run releases from the main branch.' >&2
exit 1
fi
test "$GITHUB_REF" = refs/heads/main
git fetch --tags --force
latest="$(git tag --list 'v[0-9]*' | sed -E 's/^v//' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1 || true)"
if [ -z "$latest" ]; then
version="0.0.1"
version=0.0.1
else
IFS=. read -r major minor patch <<< "$latest"
IFS=. read -r major minor patch <<<"$latest"
case "$BUMP" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
patch) patch=$((patch + 1)) ;;
*) echo "Unsupported bump: $BUMP" >&2; exit 1 ;;
esac
version="${major}.${minor}.${patch}"
fi
tag="v${version}"
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
echo "Tag already exists: $tag" >&2
exit 1
version="$major.$minor.$patch"
fi
tag="v$version"
! git rev-parse -q --verify "refs/tags/$tag" >/dev/null
plugin_version="$(sed -n -E 's/^version="([^"]+)"/\1/p' addon/plugin.cfg | head -n 1)"
if [ "$plugin_version" != "$version" ]; then
echo "addon/plugin.cfg version is $plugin_version, but the next $BUMP release is $version." >&2
echo "Update addon/plugin.cfg to version=\"$version\", commit it, then rerun this workflow." >&2
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
test "$plugin_version" = "$version"
echo "version=$version" >>"$GITHUB_OUTPUT"
echo "tag=$tag" >>"$GITHUB_OUTPUT"

# The same checks CI runs, from the same definition. This workflow used
# to package and publish without running any of them -- the only thing
# between a broken commit and the GDAM registry was whether somebody had
# looked at CI. Running them here against this exact commit is the point:
# CI passing on this SHA earlier is a claim about that run.
- name: Test
- name: Test exact release commit and package
uses: ./.github/actions/test

- name: Package addon
run: |
set -euo pipefail
test -f addon/plugin.cfg
repo_owner="${GITHUB_REPOSITORY%%/*}"
addon_dir="@${repo_owner}_${GITHUB_REPOSITORY#*/}"
package_root="dist/${addon_dir}"
mkdir -p "$package_root"
cp addon/plugin.cfg addon/plugin.gd "$package_root/"
cp addon/*.uid "$package_root/" 2>/dev/null || true
if [ -f addon/autoload.gd ]; then cp addon/autoload.gd "$package_root/"; fi
if [ -d addon/src ]; then cp -R addon/src "$package_root/"; fi
(cd "$package_root" && zip -r "../${addon_dir}.zip" .)
- name: Upload exact tested package
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: release-package-${{ github.sha }}
path: |
dist/@aviorstudio_gd-env.zip
dist/@aviorstudio_gd-env.zip.sha256
if-no-files-found: error

publish:
needs: test
runs-on: ubuntu-latest
timeout-minutes: 10
environment: release
permissions:
contents: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4

- name: Download exact tested package
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-package-${{ github.sha }}
path: dist

- name: Verify downloaded package identity
run: ./scripts/verify_package_checksum.sh

- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.release.outputs.tag }}
run: gh release create "$TAG" dist/*.zip --target "$GITHUB_SHA" --title "$TAG" --notes "Release $TAG"
TAG: ${{ needs.test.outputs.tag }}
run: gh release create "$TAG" 'dist/@aviorstudio_gd-env.zip' --target "$GITHUB_SHA" --title "$TAG" --notes "Release $TAG"

- name: Install GDAM
uses: aviorstudio/gdam-actions/install@v0.0.2
- name: Install checksum-verified GDAM v0.0.8
id: install-gdam
uses: aviorstudio/gdam-actions/install@d735444eb470194585def44521d5d91df2260e63 # v0.0.2
with:
version: 'v0.0.8'

- name: Verify GDAM version
env:
INSTALLED_VERSION: ${{ steps.install-gdam.outputs.version }}
run: |
case "$INSTALLED_VERSION" in
*0.0.8*) printf '%s\n' "$INSTALLED_VERSION" ;;
*) echo "Unexpected GDAM version: $INSTALLED_VERSION" >&2; exit 1 ;;
esac

- name: Publish to GDAM
uses: aviorstudio/gdam-actions/publish@v0.0.2
- name: Publish exact GitHub asset to GDAM
uses: aviorstudio/gdam-actions/publish@d735444eb470194585def44521d5d91df2260e63 # v0.0.2
with:
version: ${{ steps.release.outputs.version }}
tag: ${{ steps.release.outputs.tag }}
tag: ${{ needs.test.outputs.tag }}
addon: '@aviorstudio/gd-env'
asset: '@aviorstudio_gd-env.zip'
secret-key: ${{ secrets.GDAM_SECRET_KEY }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.DS_Store
dist/
.playwright-cli/
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ its response; native threaded blocking reads can otherwise stall cancellation.
The loopback regression suite exercises those paths, time scale zero, successful
completion, disabled deadlines and owner cleanup.

HTTP configuration responses are limited to 1 MiB by default. Pass the optional
final `max_body_bytes` argument to select a positive limit up to 16 MiB. The
limit is assigned to Godot's `HTTPRequest.body_size_limit` before starting the
request, so declared, chunked, and decompressed bodies are bounded during
transfer rather than checked only after accumulation. `LoadResult.error_code`
provides typed timeout, body-size, transport, HTTP-status, and JSON parse
outcomes; `body_too_large` results retain no bytes beyond the selected cap.
The 1 MiB default, 16 MiB ceiling, and existing 10-second default deadline were
approved in the decision record on
[fieldsofrevik#140](https://github.com/aviorstudio/fieldsofrevik/issues/140#issuecomment-5651934845).

## Repository Layout

- `addon/`: Godot plugin source packaged for GDAM and manual installation.
Expand All @@ -78,7 +89,16 @@ Run locally with:
./tests/test.sh
```

CI and releases run the same bounded script with a checksum-verified Godot 4.7.2 binary. Runtime errors and missing PASS markers fail the suite, including when the engine exits zero.
**Correction — [fieldsofrevik#143](https://github.com/aviorstudio/fieldsofrevik/issues/143):**
The earlier statement said CI and releases ran the same bounded script, but it
did not establish that the assembled ZIP was the package exercised by editor
lifecycle tests, and publication still used mutable action tags. CI and release
now run the same behavior and exact-package lifecycle gates with checksum-
verified Godot 4.7.2. Release transfers the tested ZIP and relative checksum to
an isolated publication job without rebuilding it, pins executable actions by
full commit SHA, and explicitly installs checksum-verified GDAM v0.0.8. Runtime
errors and missing reachable PASS markers fail the suite even when Godot exits
zero.

## License

Expand Down
2 changes: 1 addition & 1 deletion addon/plugin.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
name="GD Env"
description="Environment/config loading helpers (dotenv + JSON + OS env)."
author="Avior Studio"
version="0.0.2"
version="0.0.3"
script="plugin.gd"
Loading
Loading