Skip to content

Commit 208fce6

Browse files
authored
feat: release v0.1.0 (#45)
1 parent b526d58 commit 208fce6

8 files changed

Lines changed: 451 additions & 49 deletions

File tree

.github/workflows/ci.yml

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,102 @@ jobs:
9292
LD_LIBRARY_PATH="$PWD/target/release" \
9393
busted --lua=$(which luajit) tests/lua \
9494
--lpath='./lua/?.lua'
95+
96+
- name: Validate LuaRocks package
97+
run: |
98+
rm -rf /tmp/lua-qjson-rock
99+
ROCKSPEC="$(python3 - <<'PY'
100+
import re
101+
from pathlib import Path
102+
103+
pattern = re.compile(r"^lua-qjson-(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?-(\d+)\.rockspec$")
104+
def prerelease_key(value):
105+
if value is None:
106+
return ()
107+
key = []
108+
for part in value.split("."):
109+
if part.isdigit():
110+
key.append((0, int(part)))
111+
else:
112+
key.append((1, part))
113+
return tuple(key)
114+
115+
matches = []
116+
for path in Path("rockspec").glob("lua-qjson-*.rockspec"):
117+
match = pattern.match(path.name)
118+
if match:
119+
major, minor, patch, prerelease, revision = match.groups()
120+
matches.append((int(major), int(minor), int(patch), prerelease is None, prerelease_key(prerelease), int(revision), str(path)))
121+
if not matches:
122+
raise SystemExit("no lua-qjson rockspec found")
123+
print(max(matches)[-1])
124+
PY
125+
)"
126+
luarocks make "$ROCKSPEC" --tree /tmp/lua-qjson-rock
127+
eval "$(luarocks path --tree /tmp/lua-qjson-rock)"
128+
unset LD_LIBRARY_PATH
129+
luajit -e 'local qjson = require("qjson"); local doc = qjson.parse("{\"a\":42}"); assert(doc:get_i64("a") == 42)'
130+
busted --lua=$(which luajit) tests/lua
131+
132+
package:
133+
name: LuaRocks package (${{ matrix.os }})
134+
runs-on: ${{ matrix.os }}
135+
needs: rust
136+
strategy:
137+
matrix:
138+
os: [ubuntu-latest, macos-14]
139+
steps:
140+
- uses: actions/checkout@v4
141+
with:
142+
submodules: recursive
143+
144+
- name: Install Rust (stable)
145+
run: |
146+
rustup toolchain install stable --profile minimal --no-self-update
147+
rustup default stable
148+
149+
- name: Install LuaJIT
150+
uses: leafo/gh-actions-lua@v13
151+
with:
152+
luaVersion: "luajit-2.1.0-beta3"
153+
154+
- name: Install LuaRocks
155+
uses: leafo/gh-actions-luarocks@v4
156+
with:
157+
luarocksVersion: "3.11.1"
158+
159+
- name: Validate LuaRocks package
160+
run: |
161+
rm -rf /tmp/lua-qjson-rock
162+
ROCKSPEC="$(python3 - <<'PY'
163+
import re
164+
from pathlib import Path
165+
166+
pattern = re.compile(r"^lua-qjson-(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?-(\d+)\.rockspec$")
167+
def prerelease_key(value):
168+
if value is None:
169+
return ()
170+
key = []
171+
for part in value.split("."):
172+
if part.isdigit():
173+
key.append((0, int(part)))
174+
else:
175+
key.append((1, part))
176+
return tuple(key)
177+
178+
matches = []
179+
for path in Path("rockspec").glob("lua-qjson-*.rockspec"):
180+
match = pattern.match(path.name)
181+
if match:
182+
major, minor, patch, prerelease, revision = match.groups()
183+
matches.append((int(major), int(minor), int(patch), prerelease is None, prerelease_key(prerelease), int(revision), str(path)))
184+
if not matches:
185+
raise SystemExit("no lua-qjson rockspec found")
186+
print(max(matches)[-1])
187+
PY
188+
)"
189+
luarocks make "$ROCKSPEC" --tree /tmp/lua-qjson-rock
190+
eval "$(luarocks path --tree /tmp/lua-qjson-rock)"
191+
unset LD_LIBRARY_PATH
192+
unset DYLD_LIBRARY_PATH
193+
lua -e 'assert(jit, "LuaJIT required"); local qjson = require("qjson"); local doc = qjson.parse("{\"a\":42}"); assert(doc:get_i64("a") == 42)'

.github/workflows/release.yml

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches:
6+
- "main"
7+
paths:
8+
- "rockspec/**"
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
release:
15+
name: Release
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
with:
21+
persist-credentials: false
22+
23+
- name: Install Lua
24+
uses: leafo/gh-actions-lua@v10
25+
with:
26+
luaVersion: "5.1.5"
27+
28+
- name: Install LuaRocks
29+
uses: leafo/gh-actions-luarocks@v4
30+
with:
31+
luarocksVersion: "3.11.1"
32+
33+
- name: Install upload dependency
34+
run: luarocks install dkjson
35+
36+
- name: Extract release name
37+
id: release_env
38+
shell: bash
39+
env:
40+
HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
41+
run: |
42+
title="${HEAD_COMMIT_MESSAGE%%$'\n'*}"
43+
re="^feat: release v?([0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?)$"
44+
if [[ $title =~ $re ]]; then
45+
v=v${BASH_REMATCH[1]}
46+
echo "version=${v}" >> $GITHUB_OUTPUT
47+
echo "version_without_v=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT
48+
if [[ ${BASH_REMATCH[1]} == *-* ]]; then
49+
echo "is_prerelease=true" >> $GITHUB_OUTPUT
50+
else
51+
echo "is_prerelease=false" >> $GITHUB_OUTPUT
52+
fi
53+
else
54+
echo "commit format is not correct (expected: feat: release vX.Y.Z[-prerelease])"
55+
exit 1
56+
fi
57+
58+
- name: Verify rockspec exists
59+
id: verify_rockspec
60+
shell: bash
61+
env:
62+
VERSION_WITHOUT_V: ${{ steps.release_env.outputs.version_without_v }}
63+
run: |
64+
rockspec_file="$(python3 - <<'PY'
65+
import os
66+
import re
67+
from pathlib import Path
68+
69+
version = os.environ["VERSION_WITHOUT_V"]
70+
pattern = re.compile(r"^lua-qjson-" + re.escape(version) + r"-(\d+)\.rockspec$")
71+
matches = []
72+
for path in Path("rockspec").glob("lua-qjson-" + version + "-*.rockspec"):
73+
match = pattern.match(path.name)
74+
if match:
75+
matches.append((int(match.group(1)), str(path)))
76+
if not matches:
77+
raise SystemExit("rockspec file not found for version " + version)
78+
print(max(matches)[1])
79+
PY
80+
)"
81+
echo "rockspec_file=${rockspec_file}" >> $GITHUB_OUTPUT
82+
83+
- name: Validate LuaRocks token
84+
shell: bash
85+
env:
86+
LUAROCKS_TOKEN: ${{ secrets.LUAROCKS_TOKEN }}
87+
run: |
88+
if [ -z "$LUAROCKS_TOKEN" ]; then
89+
echo "LUAROCKS_TOKEN secret is not configured"
90+
exit 1
91+
fi
92+
93+
- name: Create Release
94+
uses: softprops/action-gh-release@v2
95+
with:
96+
tag_name: ${{ steps.release_env.outputs.version }}
97+
name: ${{ steps.release_env.outputs.version }}
98+
draft: false
99+
prerelease: ${{ steps.release_env.outputs.is_prerelease }}
100+
101+
- name: Upload to LuaRocks
102+
env:
103+
LUAROCKS_TOKEN: ${{ secrets.LUAROCKS_TOKEN }}
104+
ROCKSPEC_FILE: ${{ steps.verify_rockspec.outputs.rockspec_file }}
105+
shell: bash
106+
run: |
107+
mkdir -p "$HOME/.luarocks"
108+
chmod 700 "$HOME/.luarocks"
109+
lua -e 'local token = os.getenv("LUAROCKS_TOKEN"); assert(token and token ~= "", "missing LUAROCKS_TOKEN"); local f = assert(io.open(os.getenv("HOME") .. "/.luarocks/upload_config.lua", "w")); f:write(("key = %q\n"):format(token)); f:write("server = \"https://luarocks.org\"\nversion = \"1.0\"\n"); f:close()'
110+
chmod 600 "$HOME/.luarocks/upload_config.lua"
111+
luarocks upload "$ROCKSPEC_FILE"

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ cargo build --release
1515

1616
A `Makefile` wraps the common workflows; run `make help` to see `build`, `test`, `lint`, `bench`, and `clean` targets. Override `LUAJIT` / `LUA_CPATH` per invocation if your environment differs from the defaults.
1717

18+
## Installing
19+
20+
```sh
21+
luarocks install lua-qjson
22+
```
23+
24+
The rock builds the Rust native library during installation, so Rust/Cargo
25+
and LuaJIT must be available on the target system. The Lua module name remains
26+
`qjson`:
27+
28+
```lua
29+
local qjson = require("qjson")
30+
```
31+
1832
## Testing
1933

2034
```sh

lua/qjson.lua

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,6 @@
11
local ffi = require("ffi")
22

3-
ffi.cdef[[
4-
typedef struct qjson_doc qjson_doc;
5-
typedef struct {
6-
const qjson_doc* doc;
7-
uint32_t idx_start, idx_end, _reserved0, _reserved1;
8-
} qjson_cursor;
9-
10-
typedef struct {
11-
uint32_t mode;
12-
uint32_t max_depth;
13-
} qjson_options;
14-
15-
const char* qjson_strerror(int code);
16-
qjson_doc* qjson_parse (const uint8_t* buf, size_t len, int* err_out);
17-
qjson_doc* qjson_parse_ex(const uint8_t* buf, size_t len,
18-
const qjson_options* opts, int* err_out);
19-
void qjson_free (qjson_doc* doc);
20-
21-
int qjson_get_str (qjson_doc*, const char* path, size_t path_len, const uint8_t** p, size_t* n);
22-
int qjson_get_i64 (qjson_doc*, const char* path, size_t path_len, int64_t* out);
23-
int qjson_get_f64 (qjson_doc*, const char* path, size_t path_len, double* out);
24-
int qjson_get_bool(qjson_doc*, const char* path, size_t path_len, int* out);
25-
int qjson_is_null (qjson_doc*, const char* path, size_t path_len, int* out);
26-
int qjson_typeof (qjson_doc*, const char* path, size_t path_len, int* out);
27-
int qjson_len (qjson_doc*, const char* path, size_t path_len, size_t* out);
28-
29-
int qjson_open (qjson_doc*, const char* path, size_t path_len, qjson_cursor* out);
30-
int qjson_cursor_open (const qjson_cursor*, const char* path, size_t path_len, qjson_cursor* out);
31-
int qjson_cursor_field(const qjson_cursor*, const char* key, size_t key_len, qjson_cursor* out);
32-
int qjson_cursor_index(const qjson_cursor*, size_t i, qjson_cursor* out);
33-
34-
int qjson_cursor_get_str (const qjson_cursor*, const char*, size_t, const uint8_t**, size_t*);
35-
int qjson_cursor_get_i64 (const qjson_cursor*, const char*, size_t, int64_t*);
36-
int qjson_cursor_get_f64 (const qjson_cursor*, const char*, size_t, double*);
37-
int qjson_cursor_get_bool(const qjson_cursor*, const char*, size_t, int*);
38-
int qjson_cursor_typeof (const qjson_cursor*, const char*, size_t, int*);
39-
int qjson_cursor_len (const qjson_cursor*, const char*, size_t, size_t*);
40-
int qjson_cursor_bytes(const qjson_cursor*, size_t* byte_start, size_t* byte_end);
41-
int qjson_cursor_object_entry_at(const qjson_cursor*, size_t i,
42-
const uint8_t** key_ptr, size_t* key_len,
43-
qjson_cursor* value_out);
44-
]]
45-
46-
local C = ffi.load("qjson")
3+
local C = require("qjson.lib")
474

485
local err_box = ffi.new("int[1]")
496
local i64_box = ffi.new("int64_t[1]")

0 commit comments

Comments
 (0)