Compare commits
7 Commits
64abb39d0b
..
v1
| Author | SHA1 | Date | |
|---|---|---|---|
| 25737953d8 | |||
| b659386bd2 | |||
| c615a5fb92 | |||
| 7b9a70f4fe | |||
| c414984ba3 | |||
| 8bef637053 | |||
| 922aea9bc1 |
+32
-7
@@ -11,25 +11,50 @@ jobs:
|
|||||||
build-and-smoke:
|
build-and-smoke:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
# octowow.st is served under /git, but Gitea generates clone URLs without
|
||||||
|
# it (ROOT_URL/proxy misconfig), so actions/checkout 404s. Clone manually
|
||||||
|
# against the correct base. Remove once the server ROOT_URL is fixed.
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
run: |
|
||||||
|
git init -q .
|
||||||
|
git remote add origin "https://gitea:${{ secrets.GITHUB_TOKEN }}@octowow.st/git/${{ github.repository }}.git"
|
||||||
|
git fetch -q --depth 1 origin "${{ github.ref }}"
|
||||||
|
git checkout -q FETCH_HEAD
|
||||||
|
|
||||||
- name: Build image
|
- name: Build image
|
||||||
run: docker build -t mpq-packager:ci .
|
run: docker build -t mpq-packager:ci .
|
||||||
|
|
||||||
|
# NOTE: act_runner runs these `docker` commands against the HOST daemon via
|
||||||
|
# the mounted socket, so bind-mounting the job's files (-v "$PWD:/work")
|
||||||
|
# doesn't work — the source path isn't on the host. Use `docker cp` to
|
||||||
|
# stream files in/out of a created container instead. The GlueXML release
|
||||||
|
# workflow uses this same pattern.
|
||||||
- name: Package the fixture
|
- name: Package the fixture
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
docker run --rm \
|
cid=$(docker create \
|
||||||
-e INPUT_MANIFEST=mpq.yaml \
|
-e INPUT_MANIFEST=mpq.yaml \
|
||||||
-e INPUT_VERSION=v0.0.0-ci \
|
-e INPUT_VERSION=v0.0.0-ci \
|
||||||
-e INPUT_OUT_DIR=/work/dist \
|
-e INPUT_OUT_DIR=/work/dist \
|
||||||
-e GITHUB_WORKSPACE=/work \
|
-e GITHUB_WORKSPACE=/work \
|
||||||
-v "$PWD/test:/work" \
|
mpq-packager:ci)
|
||||||
mpq-packager:ci
|
docker cp test/. "$cid:/work"
|
||||||
|
docker start -a "$cid"
|
||||||
|
docker cp "$cid:/work/dist/patch-Z.mpq" ./patch-Z.mpq
|
||||||
|
docker rm "$cid" >/dev/null
|
||||||
|
|
||||||
- name: Assert artifact exists
|
- name: Assert archive is valid and contains the expected file
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
test -s test/dist/patch-Z.mpq
|
test -s patch-Z.mpq
|
||||||
echo "OK: $(stat -c%s test/dist/patch-Z.mpq) bytes"
|
echo "size: $(stat -c%s patch-Z.mpq) bytes"
|
||||||
|
# Read the archive back with StormLib (mpqpack --list/--cat) inside a
|
||||||
|
# sleeping container we exec into.
|
||||||
|
cid=$(docker run -d --entrypoint sleep mpq-packager:ci 120)
|
||||||
|
docker cp patch-Z.mpq "$cid:/patch-Z.mpq"
|
||||||
|
docker exec "$cid" mpqpack --list /patch-Z.mpq | tee list.txt
|
||||||
|
grep -q 'Interface\\AddOns\\Fixture\\Hello.lua' list.txt
|
||||||
|
docker exec "$cid" mpqpack --cat /patch-Z.mpq 'Interface\AddOns\Fixture\Hello.lua' \
|
||||||
|
| grep -q 'VERSION = "v0.0.0-ci"'
|
||||||
|
docker rm -f "$cid" >/dev/null
|
||||||
|
echo "OK: archive valid, path + substitution confirmed"
|
||||||
|
|||||||
@@ -2,3 +2,9 @@ dist/
|
|||||||
test/dist/
|
test/dist/
|
||||||
*.mpq
|
*.mpq
|
||||||
*.o
|
*.o
|
||||||
|
# local validation helpers (not needed in CI)
|
||||||
|
test/stub_mpqpack.py
|
||||||
|
test/mpqpack.cmd
|
||||||
|
# runner secrets/state
|
||||||
|
runner/.env
|
||||||
|
runner/data/
|
||||||
|
|||||||
+3
-1
@@ -37,6 +37,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
COPY --from=build /usr/local/bin/mpqpack /usr/local/bin/mpqpack
|
COPY --from=build /usr/local/bin/mpqpack /usr/local/bin/mpqpack
|
||||||
COPY package.py /opt/mpq-packager/package.py
|
COPY package.py /opt/mpq-packager/package.py
|
||||||
COPY entrypoint.sh /opt/mpq-packager/entrypoint.sh
|
COPY entrypoint.sh /opt/mpq-packager/entrypoint.sh
|
||||||
RUN chmod +x /opt/mpq-packager/entrypoint.sh
|
# Strip any CRLF (Windows checkouts) so the shebang and bash parse correctly.
|
||||||
|
RUN sed -i 's/\r$//' /opt/mpq-packager/entrypoint.sh /opt/mpq-packager/package.py \
|
||||||
|
&& chmod +x /opt/mpq-packager/entrypoint.sh
|
||||||
|
|
||||||
ENTRYPOINT ["/opt/mpq-packager/entrypoint.sh"]
|
ENTRYPOINT ["/opt/mpq-packager/entrypoint.sh"]
|
||||||
|
|||||||
@@ -116,5 +116,3 @@ docker run --rm \
|
|||||||
regardless of MPQ residency — packaging them here does not bypass it.
|
regardless of MPQ residency — packaging them here does not bypass it.
|
||||||
- Compression is zlib, which vanilla 1.12.1 decompresses. The output is MPQ v1
|
- Compression is zlib, which vanilla 1.12.1 decompresses. The output is MPQ v1
|
||||||
specifically; v2+ headers are not read by the 1.12 loader.
|
specifically; v2+ headers are not read by the 1.12 loader.
|
||||||
- See ClassicAPI `docs/MPQBuilding.md` for the empirical background on the
|
|
||||||
naming constraint and load-order behaviour.
|
|
||||||
|
|||||||
+40
-21
@@ -3,7 +3,15 @@
|
|||||||
# and attaches it to a matching Gitea release.
|
# and attaches it to a matching Gitea release.
|
||||||
#
|
#
|
||||||
# Requires: Actions enabled on the repo, and a runner advertising `ubuntu-latest`
|
# Requires: Actions enabled on the repo, and a runner advertising `ubuntu-latest`
|
||||||
# (or change runs-on to a label your act_runner registers).
|
# with Docker available.
|
||||||
|
#
|
||||||
|
# NOTE on octowow.st: the instance is served under the /git subpath, but Gitea
|
||||||
|
# generates URLs without it, which breaks actions/checkout and `uses:` action
|
||||||
|
# resolution. So this workflow avoids both: it clones manually against the
|
||||||
|
# correct base and runs the packager via `docker build` + `docker cp` (bind
|
||||||
|
# mounts don't work either — the inner docker uses the host daemon). Once the
|
||||||
|
# server's ROOT_URL is fixed, this can be simplified to actions/checkout +
|
||||||
|
# `uses: paste/mpq-packager@v1`.
|
||||||
|
|
||||||
name: Release MPQ
|
name: Release MPQ
|
||||||
on:
|
on:
|
||||||
@@ -15,36 +23,47 @@ jobs:
|
|||||||
package:
|
package:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout (manual; instance under /git)
|
||||||
uses: actions/checkout@v4
|
run: |
|
||||||
|
git init -q .
|
||||||
|
git remote add origin "https://gitea:${{ secrets.GITHUB_TOKEN }}@octowow.st/git/${{ github.repository }}.git"
|
||||||
|
git fetch -q --depth 1 origin "${{ github.ref }}"
|
||||||
|
git checkout -q FETCH_HEAD
|
||||||
|
|
||||||
|
- name: Build packager image
|
||||||
|
run: |
|
||||||
|
git clone -q --depth 1 --branch v1 \
|
||||||
|
"https://gitea:${{ secrets.GITHUB_TOKEN }}@octowow.st/git/paste/mpq-packager.git" /tmp/packager
|
||||||
|
docker build -q -t mpq-packager /tmp/packager
|
||||||
|
|
||||||
- name: Build patch MPQ
|
- name: Build patch MPQ
|
||||||
id: pack
|
run: |
|
||||||
uses: paste/mpq-packager@v1
|
set -euo pipefail
|
||||||
with:
|
cid=$(docker create \
|
||||||
manifest: mpq.yaml
|
-e INPUT_MANIFEST=mpq.yaml \
|
||||||
version: ${{ github.ref_name }}
|
-e INPUT_VERSION=${{ github.ref_name }} \
|
||||||
|
-e INPUT_OUT_DIR=/work/dist \
|
||||||
|
-e GITHUB_WORKSPACE=/work \
|
||||||
|
mpq-packager)
|
||||||
|
docker cp . "$cid:/work"
|
||||||
|
docker start -a "$cid"
|
||||||
|
docker cp "$cid:/work/dist/patch-Z.mpq" ./patch-Z.mpq
|
||||||
|
docker rm "$cid" >/dev/null
|
||||||
|
ls -l patch-Z.mpq
|
||||||
|
|
||||||
- name: Create release and upload asset
|
- name: Create release and upload asset
|
||||||
env:
|
env:
|
||||||
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
API="https://octowow.st/git/api/v1/repos/${{ github.repository }}"
|
||||||
TAG="${GITHUB_REF_NAME}"
|
TAG="${{ github.ref_name }}"
|
||||||
MPQ="${{ steps.pack.outputs.mpq-path }}"
|
|
||||||
NAME="${{ steps.pack.outputs.mpq-name }}"
|
|
||||||
|
|
||||||
echo "Creating release $TAG"
|
|
||||||
RID=$(curl -fsSL -X POST "$API/releases" \
|
RID=$(curl -fsSL -X POST "$API/releases" \
|
||||||
-H "Authorization: token $TOKEN" \
|
-H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"draft\":false,\"prerelease\":false}" \
|
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"draft\":false,\"prerelease\":false}" \
|
||||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
|
| python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
|
||||||
|
curl -fsSL -X POST "$API/releases/$RID/assets?name=patch-Z.mpq" \
|
||||||
echo "Uploading $NAME to release $RID"
|
|
||||||
curl -fsSL -X POST "$API/releases/$RID/assets?name=$NAME" \
|
|
||||||
-H "Authorization: token $TOKEN" \
|
-H "Authorization: token $TOKEN" \
|
||||||
-H "Content-Type: application/octet-stream" \
|
-H "Content-Type: application/octet-stream" \
|
||||||
--data-binary "@$MPQ"
|
--data-binary "@patch-Z.mpq"
|
||||||
echo "Done."
|
echo "Released $TAG with patch-Z.mpq"
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ Manifest schema (all keys optional except `contents`):
|
|||||||
extensions: [".lua", ".xml", ".toc"]
|
extensions: [".lua", ".xml", ".toc"]
|
||||||
contents:
|
contents:
|
||||||
- src: . # path in the repo (file or dir)
|
- src: . # path in the repo (file or dir)
|
||||||
dest: Interface/GlueXML # in-MPQ path (forward slashes; converted to \)
|
dest: Interface/GlueXML # in-MPQ path (forward slashes become backslashes)
|
||||||
ignore: # glob patterns, matched against repo-relative paths
|
ignore: # glob patterns, matched against repo-relative paths
|
||||||
- ".git/**"
|
- ".git/**"
|
||||||
- ".gitea/**"
|
- ".gitea/**"
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Copy to `.env` (same folder) and fill in. Do NOT commit .env — it holds the token.
|
||||||
|
# Get the token at: https://octowow.st/git/user/settings/actions/runners
|
||||||
|
# -> "Create new Runner" -> copy the registration token.
|
||||||
|
RUNNER_TOKEN=paste-registration-token-here
|
||||||
|
RUNNER_NAME=octo-windows-runner
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# act_runner setup (octowow.st)
|
||||||
|
|
||||||
|
Stands up a self-hosted Gitea Actions runner as a container. One runner
|
||||||
|
registered at the **user level** serves all of your repos (`GlueXML`,
|
||||||
|
`mpq-packager`, …).
|
||||||
|
|
||||||
|
## Prerequisites on the runner host
|
||||||
|
|
||||||
|
- **Docker** running. On Windows/macOS that means Docker Desktop with the Linux
|
||||||
|
engine (WSL2 backend on Windows). Settings → Advanced → keep "Allow the
|
||||||
|
default Docker socket to be used" enabled so the socket mount works.
|
||||||
|
|
||||||
|
## 1. Get a registration token
|
||||||
|
|
||||||
|
In the Gitea web UI, as your user:
|
||||||
|
|
||||||
|
> avatar → **Settings** → **Actions** → **Runners** → **Create new Runner**
|
||||||
|
|
||||||
|
Copy the **registration token** (a long string). This is user-scoped, so the
|
||||||
|
runner picks up jobs from any of your repos.
|
||||||
|
|
||||||
|
Direct link: <https://octowow.st/git/user/settings/actions/runners>
|
||||||
|
|
||||||
|
> A repo-scoped token (repo → Settings → Actions → Runners) also works if you
|
||||||
|
> only want the runner tied to one repo.
|
||||||
|
|
||||||
|
## 2. Configure
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp .env.example .env
|
||||||
|
# edit .env: paste RUNNER_TOKEN, set RUNNER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Start
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker compose up -d
|
||||||
|
docker compose logs -f # watch it register
|
||||||
|
```
|
||||||
|
|
||||||
|
The runner should appear as **Idle / online** in the Runners list within a few
|
||||||
|
seconds.
|
||||||
|
|
||||||
|
## 4. Enable Actions on each repo
|
||||||
|
|
||||||
|
Per repo: **Settings → Actions →** enable. New repos have it off by default.
|
||||||
|
|
||||||
|
## 5. Verify
|
||||||
|
|
||||||
|
Push a tag (or run a workflow) and watch it execute under the **Actions** tab.
|
||||||
|
For `mpq-packager`'s release flow, a `v*` tag on a consuming repo builds the MPQ
|
||||||
|
and attaches it to a release.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- **First job is slow** — it pulls `catthehacker/ubuntu:act-latest` (~1 GB) the
|
||||||
|
first time; cached afterward.
|
||||||
|
- **`runs-on` labels** must match `GITEA_RUNNER_LABELS`. We map
|
||||||
|
`ubuntu-latest` → `catthehacker/ubuntu:act-latest`. Add more pairs
|
||||||
|
(comma-separated) to support other labels.
|
||||||
|
- **Docker actions** (e.g. `uses: paste/mpq-packager@v1`) work out of the box:
|
||||||
|
act_runner builds and runs them via the mounted host socket.
|
||||||
|
- **Workflows that run `docker` CLI in a step** (like this repo's own
|
||||||
|
`.gitea/workflows/ci.yml`) need the job container to reach Docker too. The
|
||||||
|
release workflow does not — it only *uses* the Docker action — so it's fine.
|
||||||
|
Ask if you want the CI's docker-in-job case wired up.
|
||||||
|
- **Re-registering**: delete `./data` and `docker compose up -d` again with a
|
||||||
|
fresh token.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# act_runner for octowow.st, run as a container (Docker Compose).
|
||||||
|
# Copy this folder to the runner host, fill in .env (see .env.example),
|
||||||
|
# then: docker compose up -d
|
||||||
|
#
|
||||||
|
# The runner auto-registers on first start using the env vars below, then
|
||||||
|
# persists its credentials in ./data so restarts don't re-register.
|
||||||
|
|
||||||
|
services:
|
||||||
|
act_runner:
|
||||||
|
image: gitea/act_runner:latest
|
||||||
|
container_name: octo-act-runner
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
# Gitea base URL — note the /git path on this instance.
|
||||||
|
GITEA_INSTANCE_URL: https://octowow.st/git
|
||||||
|
# Registration token from Settings -> Actions -> Runners -> "Create new Runner".
|
||||||
|
GITEA_RUNNER_REGISTRATION_TOKEN: ${RUNNER_TOKEN}
|
||||||
|
GITEA_RUNNER_NAME: ${RUNNER_NAME:-octo-runner}
|
||||||
|
# runs-on label -> image mapping. ubuntu-latest uses the de-facto act image.
|
||||||
|
GITEA_RUNNER_LABELS: ubuntu-latest:docker://catthehacker/ubuntu:act-latest
|
||||||
|
volumes:
|
||||||
|
# Host Docker socket: act_runner spawns job/action containers as siblings.
|
||||||
|
# On Docker Desktop (Windows/Mac) this path is provided by the Linux engine.
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
# Persist runner registration + state.
|
||||||
|
- ./data:/data
|
||||||
+42
-1
@@ -80,9 +80,50 @@ static unsigned walk(const char *dir, int add) {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --list <mpq>: enumerate archive contents (names + sizes). */
|
||||||
|
static int cmd_list(const char *mpq) {
|
||||||
|
HANDLE h;
|
||||||
|
if (!SFileOpenArchive(mpq, 0, STREAM_FLAG_READ_ONLY, &h)) die("open", mpq);
|
||||||
|
SFILE_FIND_DATA fd;
|
||||||
|
HANDLE find = SFileFindFirstFile(h, "*", &fd, NULL);
|
||||||
|
if (find) {
|
||||||
|
do {
|
||||||
|
printf("%-50s %u\n", fd.cFileName, fd.dwFileSize);
|
||||||
|
} while (SFileFindNextFile(find, &fd));
|
||||||
|
SFileFindClose(find);
|
||||||
|
}
|
||||||
|
SFileCloseArchive(h);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --cat <mpq> <internal-name>: dump one file's bytes to stdout. */
|
||||||
|
static int cmd_cat(const char *mpq, const char *name) {
|
||||||
|
HANDLE h, f;
|
||||||
|
if (!SFileOpenArchive(mpq, 0, STREAM_FLAG_READ_ONLY, &h)) die("open", mpq);
|
||||||
|
if (!SFileOpenFileEx(h, name, 0, &f)) die("openfile", name);
|
||||||
|
char buf[65536];
|
||||||
|
DWORD got;
|
||||||
|
while (SFileReadFile(f, buf, sizeof buf, &got, NULL) || got) {
|
||||||
|
fwrite(buf, 1, got, stdout);
|
||||||
|
if (got < sizeof buf) break;
|
||||||
|
}
|
||||||
|
SFileCloseFile(f);
|
||||||
|
SFileCloseArchive(h);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
if (argc >= 3 && !strcmp(argv[1], "--list"))
|
||||||
|
return cmd_list(argv[2]);
|
||||||
|
if (argc >= 4 && !strcmp(argv[1], "--cat"))
|
||||||
|
return cmd_cat(argv[2], argv[3]);
|
||||||
|
|
||||||
if (argc < 3) {
|
if (argc < 3) {
|
||||||
fprintf(stderr, "usage: %s <output.mpq> <staging-dir> [maxFiles]\n", argv[0]);
|
fprintf(stderr,
|
||||||
|
"usage: %s <output.mpq> <staging-dir> [maxFiles]\n"
|
||||||
|
" %s --list <mpq>\n"
|
||||||
|
" %s --cat <mpq> <internal-name>\n",
|
||||||
|
argv[0], argv[0], argv[0]);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
const char *out = argv[1];
|
const char *out = argv[1];
|
||||||
|
|||||||
Reference in New Issue
Block a user