From bf158719b51cea87867bfeec5543110baafd0301 Mon Sep 17 00:00:00 2001 From: omer-topal Date: Wed, 22 Apr 2026 21:22:23 +0300 Subject: [PATCH] feat: auto release with permify tag --- .github/workflows/auto-release.yml | 70 +++++++++++++++ .github/workflows/generator.yml | 24 ++++- .github/workflows/npm-publish.yml | 5 +- docker-compose.yaml | 7 ++ generator/generate-sdk.sh | 7 +- integration/live.spec.js | 139 +++++++++++++++++++++++++++++ package.json | 5 +- scripts/commit-changes.sh | 2 +- scripts/run-live-tests.sh | 29 ++++++ 9 files changed, 281 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/auto-release.yml create mode 100644 docker-compose.yaml create mode 100644 integration/live.spec.js create mode 100755 scripts/run-live-tests.sh diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml new file mode 100644 index 0000000..7cc7066 --- /dev/null +++ b/.github/workflows/auto-release.yml @@ -0,0 +1,70 @@ +name: Auto Release + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - '.openapi-generator/**' + - 'docs/**' + - 'src/**' + - 'test/**' + - 'package.json' + - 'README.md' + +permissions: + contents: write + +jobs: + release: + name: Create SDK Release + timeout-minutes: 10 + runs-on: ubuntu-latest + + steps: + - name: Harden Runner + uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + with: + fetch-depth: 0 + + - name: Create tag and release + env: + GH_TOKEN: ${{ secrets.PAT_TOKEN }} + run: | + PACKAGE_VERSION="$(sed -n 's/^[[:space:]]*"version":[[:space:]]*"\([^"]*\)".*/\1/p' package.json | head -n 1)" + OPENAPI_VERSION="$(sed -n 's/.*"version":[[:space:]]*"\([^"]*\)".*/\1/p' generator/openapi.json | head -n 1)" + OPENAPI_PACKAGE_VERSION="${OPENAPI_VERSION#v}" + + if [ -z "${PACKAGE_VERSION}" ]; then + echo "Package version is empty. Skipping release creation." + exit 0 + fi + + if [ -n "${OPENAPI_PACKAGE_VERSION}" ] && [ "${PACKAGE_VERSION}" != "${OPENAPI_PACKAGE_VERSION}" ]; then + echo "Package version (${PACKAGE_VERSION}) does not match OpenAPI version (${OPENAPI_PACKAGE_VERSION}). Skipping release creation." + exit 0 + fi + + TAG_NAME="v${PACKAGE_VERSION}" + + if git rev-parse -q --verify "refs/tags/${TAG_NAME}" >/dev/null; then + echo "Tag ${TAG_NAME} already exists. Skipping release creation." + exit 0 + fi + + echo "Creating release ${TAG_NAME}" + git config --global user.name "GitHub Actions Bot" + git config --global user.email "<>" + git tag -a "${TAG_NAME}" -m "Release ${TAG_NAME}" + git push origin "${TAG_NAME}" + + gh release create "${TAG_NAME}" \ + --repo "${GITHUB_REPOSITORY}" \ + --title "${TAG_NAME}" \ + --generate-notes diff --git a/.github/workflows/generator.yml b/.github/workflows/generator.yml index 3f18577..62aa0c1 100644 --- a/.github/workflows/generator.yml +++ b/.github/workflows/generator.yml @@ -37,12 +37,23 @@ jobs: distribution: temurin java-version: 17 + - name: Setup Node.js + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 + with: + node-version: 20 + # Generate the SDK - name: Generate JavaScript SDK run: | chmod +x generator/generate-sdk.sh generator/generate-sdk.sh + - name: Install dependencies + run: npm install --no-package-lock + + - name: Run smoke tests + run: npm test + # Commit changes and open PR if there are changes - name: Commit changes id: commitchanges @@ -68,6 +79,17 @@ jobs: if [ -n "${PR_NUMBER}" ]; then gh pr edit "${PR_NUMBER}" --title "${PR_TITLE}" --body "${PR_BODY}" else - gh pr create --base main --head "${BRANCH_NAME}" --title "${PR_TITLE}" --body "${PR_BODY}" --label dependencies --label automated + CREATE_ARGS=(--base main --head "${BRANCH_NAME}" --title "${PR_TITLE}" --body "${PR_BODY}") + LABELS="$(gh label list --json name --jq '.[].name' || true)" + + if printf '%s\n' "${LABELS}" | grep -qx 'dependencies'; then + CREATE_ARGS+=(--label dependencies) + fi + + if printf '%s\n' "${LABELS}" | grep -qx 'automated'; then + CREATE_ARGS+=(--label automated) + fi + + gh pr create "${CREATE_ARGS[@]}" fi shell: bash diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 6f2dd0a..773cf04 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -29,11 +29,14 @@ jobs: registry-url: "https://registry.npmjs.org" - name: Install dependencies - run: npm install + run: npm install --no-package-lock - name: Build run: npm run build + - name: Run smoke tests + run: npm test + - name: Write release version run: | VERSION=${GITHUB_REF_NAME#v} diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..77c940f --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,7 @@ +services: + permify: + image: "ghcr.io/permify/permify:v1.6.9" + ports: + - "3476:3476" + - "3478:3478" + command: "serve" diff --git a/generator/generate-sdk.sh b/generator/generate-sdk.sh index f36ce86..935fd5e 100755 --- a/generator/generate-sdk.sh +++ b/generator/generate-sdk.sh @@ -6,10 +6,11 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="${SCRIPT_DIR}/.." OPENAPI_FILE="${SCRIPT_DIR}/openapi.json" GENERATOR_VERSION="7.13.0" -PACKAGE_VERSION="$(sed -n 's/^[[:space:]]*"version":[[:space:]]*"\([^"]*\)".*/\1/p' "${PROJECT_ROOT}/package.json" | head -n 1)" +OPENAPI_VERSION="$(sed -n 's/.*"version":[[:space:]]*"\([^"]*\)".*/\1/p' "${OPENAPI_FILE}" | head -n 1)" +PACKAGE_VERSION="${OPENAPI_VERSION#v}" -if [[ -z "${PACKAGE_VERSION}" ]]; then - echo "Could not determine package version from ${PROJECT_ROOT}/package.json" >&2 +if [[ -z "${OPENAPI_VERSION}" || -z "${PACKAGE_VERSION}" ]]; then + echo "Could not determine package version from ${OPENAPI_FILE}" >&2 exit 1 fi diff --git a/integration/live.spec.js b/integration/live.spec.js new file mode 100644 index 0000000..9e1e6bd --- /dev/null +++ b/integration/live.spec.js @@ -0,0 +1,139 @@ +import assert from 'assert'; +import * as PermifyApi from '../src/index'; + +const PERMIFY_BASE_URL = process.env.PERMIFY_BASE_URL || 'http://127.0.0.1:3476'; +const TENANT_ID = 't1'; +const RUN_ID = Date.now().toString(36); + +function createApis() { + const apiClient = new PermifyApi.ApiClient(PERMIFY_BASE_URL); + apiClient.timeout = 10000; + + return { + schema: new PermifyApi.SchemaApi(apiClient), + data: new PermifyApi.DataApi(apiClient), + permission: new PermifyApi.PermissionApi(apiClient), + }; +} + +describe('Permify live REST tests', function() { + this.timeout(20000); + + it('denies a permission check when no relationship exists', async function() { + const apis = createApis(); + const documentId = `denieddoc${RUN_ID}`; + const subjectId = `denieduser${RUN_ID}`; + const schema = ` + entity user {} + + entity document { + relation viewer @user + + action view = viewer + } + `; + + const schemaWrite = await apis.schema.schemasWrite(TENANT_ID, { + schema, + }); + + const check = await apis.permission.permissionsCheck(TENANT_ID, { + metadata: { + snap_token: '', + schema_version: schemaWrite.schema_version, + depth: 20, + }, + entity: { + type: 'document', + id: documentId, + }, + permission: 'view', + subject: { + type: 'user', + id: subjectId, + }, + }); + + assert.strictEqual(check.can, 'CHECK_RESULT_DENIED'); + }); + + it('looks up entities a subject can view', async function() { + const apis = createApis(); + const subjectId = `lookupuser${RUN_ID}`; + const expectedEntityIds = [ + `lookupdoca${RUN_ID}`, + `lookupdocb${RUN_ID}`, + `lookupdocc${RUN_ID}`, + ]; + const schema = ` + entity user {} + + entity document { + relation viewer @user + + action view = viewer + } + `; + + const schemaWrite = await apis.schema.schemasWrite(TENANT_ID, { + schema, + }); + + const dataWrite = await apis.data.dataWrite(TENANT_ID, { + metadata: { + schema_version: schemaWrite.schema_version, + }, + tuples: [ + { + entity: { + type: 'document', + id: expectedEntityIds[0], + }, + relation: 'viewer', + subject: { + type: 'user', + id: subjectId, + }, + }, + { + entity: { + type: 'document', + id: expectedEntityIds[1], + }, + relation: 'viewer', + subject: { + type: 'user', + id: subjectId, + }, + }, + { + entity: { + type: 'document', + id: expectedEntityIds[2], + }, + relation: 'viewer', + subject: { + type: 'user', + id: subjectId, + }, + }, + ], + }); + + const lookup = await apis.permission.permissionsLookupEntity(TENANT_ID, { + metadata: { + snap_token: dataWrite.snap_token, + schema_version: schemaWrite.schema_version, + depth: 20, + }, + entity_type: 'document', + permission: 'view', + subject: { + type: 'user', + id: subjectId, + }, + }); + + assert.deepStrictEqual(lookup.entity_ids.sort(), expectedEntityIds.sort()); + }); +}); diff --git a/package.json b/package.json index 86844bf..9511470 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,10 @@ "scripts": { "build": "babel src -d dist", "prepare": "npm run build", - "test": "mocha --require @babel/register --recursive" + "test": "mocha --require @babel/register --recursive", + "test:live": "mocha --require @babel/register integration/**/*.spec.js", + "run-live-tests": "./scripts/run-live-tests.sh", + "run-instance": "./scripts/run-live-tests.sh" }, "browser": { "fs": false diff --git a/scripts/commit-changes.sh b/scripts/commit-changes.sh index 64834d4..4c89ce3 100755 --- a/scripts/commit-changes.sh +++ b/scripts/commit-changes.sh @@ -4,7 +4,7 @@ set -euo pipefail branch_name="${1:?branch name is required}" -if git diff --quiet; then +if [[ -z "$(git status --porcelain)" ]]; then echo "changes_made=0" >> "${GITHUB_OUTPUT}" echo "No changes detected" exit 0 diff --git a/scripts/run-live-tests.sh b/scripts/run-live-tests.sh new file mode 100755 index 0000000..809b0d0 --- /dev/null +++ b/scripts/run-live-tests.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +COMPOSE_FILE="${ROOT_DIR}/docker-compose.yaml" +PERMIFY_BASE_URL="${PERMIFY_BASE_URL:-http://127.0.0.1:3476}" + +cleanup() { + docker compose -f "${COMPOSE_FILE}" down -v >/dev/null 2>&1 || true +} + +trap cleanup EXIT + +cd "${ROOT_DIR}" + +docker compose -f "${COMPOSE_FILE}" up -d + +for attempt in {1..60}; do + if curl -s -o /dev/null "${PERMIFY_BASE_URL}/healthz"; then + npm run test:live + exit 0 + fi + + sleep 1 +done + +echo "Permify did not become reachable at ${PERMIFY_BASE_URL} within 60 seconds." >&2 +exit 1