diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ebf23ba --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,133 @@ +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: "Release version, for example 0.1.0 or v0.1.0" + required: true + push: + branches: + - "main" + paths: + - "rockspec/**" + +permissions: + contents: write + +jobs: + release: + if: "${{ github.event_name == 'workflow_dispatch' || startsWith(github.event.head_commit.message, 'feat: release ') }}" + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + persist-credentials: false + + - name: Install Lua + uses: leafo/gh-actions-lua@35bcb06abec04ec87df82e08caa84d545348536e + with: + luaVersion: "5.1.5" + + - name: Install LuaRocks + uses: leafo/gh-actions-luarocks@e65774a6386cb4f24e293dca7fc4ff89165b64c5 + with: + luarocksVersion: "3.11.1" + + - name: Install upload dependency + run: luarocks install dkjson + + - name: Extract release name + id: release_env + shell: bash + env: + HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + RELEASE_VERSION: ${{ inputs.version }} + run: | + semver_re="^v?([0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?)$" + if [ -n "$RELEASE_VERSION" ]; then + if [[ $RELEASE_VERSION =~ $semver_re ]]; then + version="${BASH_REMATCH[1]}" + else + echo "version input is not correct (expected: X.Y.Z[-prerelease])" + exit 1 + fi + else + title="${HEAD_COMMIT_MESSAGE%%$'\n'*}" + if [[ $title =~ ^(.*)\ \(#[0-9]+\)$ ]]; then + title="${BASH_REMATCH[1]}" + fi + title_re="^feat: release v?([0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?)$" + if [[ $title =~ $title_re ]]; then + version="${BASH_REMATCH[1]}" + else + echo "commit format is not correct (expected: feat: release vX.Y.Z[-prerelease])" + exit 1 + fi + fi + + echo "version=v${version}" >> $GITHUB_OUTPUT + echo "version_without_v=${version}" >> $GITHUB_OUTPUT + if [[ $version == *-* ]]; then + echo "is_prerelease=true" >> $GITHUB_OUTPUT + else + echo "is_prerelease=false" >> $GITHUB_OUTPUT + fi + + - name: Verify rockspec exists + id: verify_rockspec + shell: bash + env: + VERSION_WITHOUT_V: ${{ steps.release_env.outputs.version_without_v }} + run: | + rockspec_file="$(python3 - <<'PY' + import os + import re + from pathlib import Path + + version = os.environ["VERSION_WITHOUT_V"] + pattern = re.compile(r"^api7-lua-resty-simdjson-" + re.escape(version) + r"-(\d+)\.rockspec$") + matches = [] + for path in Path("rockspec").glob("api7-lua-resty-simdjson-" + version + "-*.rockspec"): + match = pattern.match(path.name) + if match: + matches.append((int(match.group(1)), str(path))) + if not matches: + raise SystemExit("rockspec file not found for version " + version) + print(max(matches)[1]) + PY + )" + echo "rockspec_file=${rockspec_file}" >> $GITHUB_OUTPUT + + - name: Validate LuaRocks token + shell: bash + env: + LUAROCKS_TOKEN: ${{ secrets.LUAROCKS_TOKEN }} + run: | + if [ -z "$LUAROCKS_TOKEN" ]; then + echo "LUAROCKS_TOKEN secret is not configured" + exit 1 + fi + + - name: Create Release + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 + with: + tag_name: ${{ steps.release_env.outputs.version }} + name: ${{ steps.release_env.outputs.version }} + draft: false + prerelease: ${{ steps.release_env.outputs.is_prerelease }} + + - name: Upload to LuaRocks + env: + LUAROCKS_TOKEN: ${{ secrets.LUAROCKS_TOKEN }} + ROCKSPEC_FILE: ${{ steps.verify_rockspec.outputs.rockspec_file }} + shell: bash + run: | + mkdir -p "$HOME/.luarocks" + chmod 700 "$HOME/.luarocks" + touch "$HOME/.luarocks/upload_config.lua" + chmod 600 "$HOME/.luarocks/upload_config.lua" + 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")); assert(f:write(("key = %q\n"):format(token))); assert(f:write("server = \"https://luarocks.org\"\nversion = \"1.0\"\n")); assert(f:close())' + luarocks upload "$ROCKSPEC_FILE" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c0328e3..0bac405 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: - name: Install packages run: | sudo apt update - sudo apt-get install -qq -y wget cpanminus net-tools libpcre3-dev build-essential valgrind + sudo apt-get install -qq -y wget cpanminus net-tools libpcre3-dev build-essential valgrind lua5.1 liblua5.1-0-dev luarocks if [ ! -e perl ]; then sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1); cp -r /usr/local/share/perl/ .; else sudo cp -r perl /usr/local/share; fi - name: Download OpenResty @@ -67,6 +67,41 @@ jobs: openresty -V make test OPENRESTY_PREFIX=${BASE_PATH}/openresty DEBUG=true + - name: Validate LuaRocks package + run: | + export PATH=${BASE_PATH}/openresty/bin:$PATH + rm -rf /tmp/lua-resty-simdjson-rock + ROCKSPEC="$(python3 - <<'PY' + import re + from pathlib import Path + + pattern = re.compile(r"^api7-lua-resty-simdjson-(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?-(\d+)\.rockspec$") + def prerelease_key(value): + if value is None: + return () + key = [] + for part in value.split("."): + if part.isdigit(): + key.append((0, int(part))) + else: + key.append((1, part)) + return tuple(key) + + matches = [] + for path in Path("rockspec").glob("api7-lua-resty-simdjson-*.rockspec"): + match = pattern.match(path.name) + if match: + major, minor, patch, prerelease, revision = match.groups() + matches.append((int(major), int(minor), int(patch), prerelease is None, prerelease_key(prerelease), int(revision), str(path))) + if not matches: + raise SystemExit("no api7-lua-resty-simdjson rockspec found") + print(max(matches)[-1]) + PY + )" + luarocks --lua-version=5.1 make "$ROCKSPEC" --tree /tmp/lua-resty-simdjson-rock + eval "$(luarocks --lua-version=5.1 path --tree /tmp/lua-resty-simdjson-rock)" + resty -e 'local simdjson = require("resty.simdjson"); local parser = assert(simdjson.new()); local obj = parser:decode("{\"a\":42}"); assert(obj.a == 42); assert(parser:encode(obj) == "{\"a\":42}")' + - name: Run Valgrind run: | export PATH=${BASE_PATH}/openresty/bin:$PATH diff --git a/rockspec/api7-lua-resty-simdjson-0.1.0-1.rockspec b/rockspec/api7-lua-resty-simdjson-0.1.0-1.rockspec new file mode 100644 index 0000000..5873cb8 --- /dev/null +++ b/rockspec/api7-lua-resty-simdjson-0.1.0-1.rockspec @@ -0,0 +1,39 @@ +package = "api7-lua-resty-simdjson" +version = "0.1.0-1" +supported_platforms = { "linux", "macosx" } + +source = { + url = "git+https://github.com/api7/lua-resty-simdjson.git", + tag = "v0.1.0", +} + +description = { + summary = "Fast JSON encoder and decoder for OpenResty", + detailed = [[ + lua-resty-simdjson provides a LuaJIT FFI binding for simdjson. + It is optimized for decoding large JSON payloads in OpenResty while + keeping proxy path latency low through optional yielding. + ]], + homepage = "https://github.com/api7/lua-resty-simdjson", + license = "Apache-2.0", +} + +dependencies = { + "lua == 5.1", + "lua-cjson", + "lua-resty-core", +} + +build = { + type = "command", + build_command = "make build", + install_command = [[ + set -e + mkdir -p "$(LUADIR)/resty/simdjson" "$(LIBDIR)" + cp lib/resty/simdjson/*.lua "$(LUADIR)/resty/simdjson/" + lib=libsimdjson_ffi.so + if [ ! -f "$lib" ]; then lib=libsimdjson_ffi.dylib; fi + if [ ! -f "$lib" ]; then echo "simdjson native library not found"; exit 1; fi + cp "$lib" "$(LIBDIR)/" + ]], +}