Skip to content
Draft
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
51 changes: 51 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Bug report
description: Report a reproducible problem in Perspectica.
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Do not include provider keys, ChatGPT tokens, private article text, or exported logs with secrets.
- type: input
id: version
attributes:
label: Perspectica version
placeholder: v0.1.0 or development commit
validations:
required: true
- type: input
id: browser
attributes:
label: Browser and version
placeholder: Chrome 140 on macOS 15
validations:
required: true
- type: textarea
id: steps
attributes:
label: Reproduction steps
description: Include the provider choice and whether the issue survives a fresh profile.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual behavior and sanitized diagnostics
validations:
required: true
- type: checkboxes
id: checks
attributes:
label: Safety checks
options:
- label: I removed credentials, tokens, private article text, and sensitive URLs.
required: true
- label: This is not a security vulnerability; I will use private reporting for security issues.
required: true
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Private security report
url: https://github.com/
about: Use the repository owner's private vulnerability-reporting channel; do not disclose exploit details publicly.
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Feature request
description: Suggest a user-facing improvement or developer change.
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: Problem to solve
description: Who is affected and what outcome is missing?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed behavior
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
- type: checkboxes
id: boundaries
attributes:
label: Product boundaries
options:
- label: This request does not require silently installing browser policy or automating a provider website.
required: true
22 changes: 22 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Summary

<!-- What changed and why? Keep runtime, permissions, and release-impact details explicit. -->

## Validation

- [ ] `pnpm verify`
- [ ] `pnpm verify:release` (when packaging or manifest behavior changes)
- [ ] `pnpm audit --prod --audit-level=high`
- [ ] `cargo test --manifest-path tools/installer/Cargo.toml --locked` (when installer changes)

## Security and privacy review

- [ ] No credentials, raw article corpus, local profiles, or generated release output committed.
- [ ] New host permissions are documented in `docs/permissions.md` and requested only with a user gesture.
- [ ] Provider/network behavior is documented in `docs/provider-boundaries.md`.
- [ ] This change does not automate ChatGPT UI, parse DuckDuckGo HTML, or add remote executable code.
- [ ] Store listing, privacy, threat-model, or retention docs updated if behavior changed.

## Release notes

<!-- Mention migration, rollback, browser support, and user-visible behavior when relevant. -->
9 changes: 6 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ jobs:
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
with:
version: 9.15.4

- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
cache: pnpm
Expand All @@ -44,3 +44,6 @@ jobs:

- name: Audit production dependencies
run: pnpm audit --prod --audit-level=high

- name: Test release installer
run: cargo test --manifest-path tools/installer/Cargo.toml --locked
41 changes: 41 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Documentation Pages

on:
push:
branches: [main]
paths:
- "docs/site/**"
- ".github/workflows/pages.yml"
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true

jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Configure Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5

- name: Upload static support pages
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
with:
path: docs/site

- name: Deploy Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
211 changes: 211 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: write
id-token: write
attestations: write

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
extension:
name: Verify and package extension
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
with:
version: 9.15.4

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
cache: pnpm

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

- name: Verify release tag and package versions
env:
TAG: ${{ github.ref_name }}
run: node scripts/check-release-version.mjs "$TAG"

- name: Audit production dependencies
run: pnpm audit --prod --audit-level=high

- name: Verify and package production extension
run: pnpm verify:release

- name: Verify generated manifest version
shell: bash
env:
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
expected="${TAG#v}"
EXPECTED_VERSION="$expected" node <<'NODE'
const fs = require("node:fs");
const expected = process.env.EXPECTED_VERSION;
const manifest = JSON.parse(
fs.readFileSync("apps/extension/.output/chrome-mv3/manifest.json", "utf8"),
);
if (manifest.version !== expected) {
throw new Error(
`generated manifest version ${manifest.version} does not match ${expected}`,
);
}
console.log(`Generated manifest version ${expected} matches release tag.`);
NODE

- name: Prepare versioned extension artifact
id: extension
shell: bash
env:
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
mkdir -p release
source_zip="$(find apps/extension/.output -maxdepth 1 -type f -name '*.zip' -print -quit)"
test -n "$source_zip"
zip_name="perspectica-extension-${TAG}.zip"
cp "$source_zip" "release/$zip_name"
echo "zip_name=$zip_name" >> "$GITHUB_OUTPUT"

- name: Generate SPDX SBOM
uses: anchore/sbom-action@d94f46e13c6c62f59525ac9a1e147a99dc0b9bf5 # v0.17.0
with:
path: release/${{ steps.extension.outputs.zip_name }}
format: spdx-json
output-file: release/perspectica-sbom.spdx.json

- name: Upload extension artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: release-extension
path: release/
if-no-files-found: error
retention-days: 14

installer-macos:
name: Build universal macOS helper
runs-on: macos-14
timeout-minutes: 20
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Add Rust targets
run: rustup target add x86_64-apple-darwin aarch64-apple-darwin

- name: Test helper
run: cargo test --manifest-path tools/installer/Cargo.toml --locked

- name: Build universal helper
shell: bash
env:
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
cargo build --manifest-path tools/installer/Cargo.toml --locked --release --target x86_64-apple-darwin
cargo build --manifest-path tools/installer/Cargo.toml --locked --release --target aarch64-apple-darwin
mkdir -p release/perspectica-installer-macos
lipo -create \
tools/installer/target/x86_64-apple-darwin/release/perspectica-installer \
tools/installer/target/aarch64-apple-darwin/release/perspectica-installer \
-output release/perspectica-installer-macos/perspectica-installer
chmod +x release/perspectica-installer-macos/perspectica-installer
cp tools/installer/README.md release/perspectica-installer-macos/README.md
tar -C release -czf "release/perspectica-installer-macos-universal-${TAG}.tar.gz" perspectica-installer-macos
rm -rf release/perspectica-installer-macos

- name: Upload macOS helper
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: release-installer-macos
path: release/
if-no-files-found: error
retention-days: 14

installer-windows:
name: Build Windows helper
runs-on: windows-latest
timeout-minutes: 20
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Test helper
run: cargo test --manifest-path tools/installer/Cargo.toml --locked

- name: Build Windows helper
shell: pwsh
env:
TAG: ${{ github.ref_name }}
run: |
cargo build --manifest-path tools/installer/Cargo.toml --locked --release
New-Item -ItemType Directory -Force release/perspectica-installer-windows | Out-Null
Copy-Item tools/installer/target/release/perspectica-installer.exe release/perspectica-installer-windows/
Copy-Item tools/installer/README.md release/perspectica-installer-windows/README.md
Compress-Archive -Path release/perspectica-installer-windows/* -DestinationPath "release/perspectica-installer-windows-x64-$env:TAG.zip"
Remove-Item -Recurse -Force release/perspectica-installer-windows

- name: Upload Windows helper
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: release-installer-windows
path: release/
if-no-files-found: error
retention-days: 14

publish:
name: Attest and publish release
needs: [extension, installer-macos, installer-windows]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Download release artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: release-*
path: release
merge-multiple: true

- name: Generate checksums
run: |
set -euo pipefail
cd release
find . -maxdepth 1 -type f ! -name SHA256SUMS -print0 \
| sort -z \
| xargs -0 sha256sum \
| sed 's# \./# #' > SHA256SUMS

- name: Attest release files
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
with:
subject-path: release/*

- name: Upload release evidence bundle
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: perspectica-${{ github.ref_name }}-release-evidence
path: release/
if-no-files-found: error
retention-days: 14

- name: Publish GitHub release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
run: gh release create "$TAG" release/* --verify-tag --generate-notes --title "Perspectica $TAG"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
**/data/
**/dist/
**/node_modules/
**/target/
**/*.tsbuildinfo
.turbo/
.playwright-cli/
Expand Down
Loading