Skip to content

Commit c87bc67

Browse files
committed
chore: 📄 prepare repo for OSS launch
## Summary Comprehensive pass to make the repo safe and useful to flip to public visibility. - Apache-2.0 LICENSE + NOTICE; Contributor Covenant 2.1; SECURITY policy. - Doc sweep: removed stale Authentik/AppAuth/OIDC/JWT/PKCE references; rewrote auth as CWA-Basic-proxy across README, architecture, sync-api, development. - README rewrite: motivation, privacy stance, badges, status table, accurate quickstart. - CONTRIBUTING, issue templates (bug/feature/config), PR template. - Release signing wired (env-var-driven keystore, falls back to debug-signed); tag-triggered GitHub Release job; full process documented in \`docs/release.md\`. - CI/CD hardening: Renovate config, CodeQL (java-kotlin + python), Trivy + SBOM + provenance for the server image, all action references pinned to commit SHAs. - Self-hosting: \`server/docker-compose.yml\` + \`.env.example\`. - In-app open-source-licenses screen (AboutLibraries) reachable from Settings. - F-Droid fastlane metadata (title, descriptions, initial changelog; asset directories stubbed with READMEs). - \`.gitignore\` hardened against keystore/secret commits.
1 parent 0a1832d commit c87bc67

47 files changed

Lines changed: 1562 additions & 187 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Bug report
2+
description: Something is broken in the Android app or the sync server.
3+
labels: ["bug"]
4+
body:
5+
- type: markdown
6+
attributes:
7+
value: |
8+
Thanks for taking the time to report a bug. Please fill in as
9+
much as you can — vague reports often can't be acted on.
10+
- type: dropdown
11+
id: component
12+
attributes:
13+
label: Component
14+
options:
15+
- Android app
16+
- Sync server
17+
- Both / unsure
18+
- Documentation
19+
validations:
20+
required: true
21+
- type: input
22+
id: version
23+
attributes:
24+
label: Version / commit
25+
description: App version (Settings → About) or server git SHA.
26+
placeholder: 2026.05.07.42 or git sha abc1234
27+
validations:
28+
required: true
29+
- type: textarea
30+
id: what-happened
31+
attributes:
32+
label: What happened?
33+
description: What did you do, what did you expect, what did you see instead?
34+
validations:
35+
required: true
36+
- type: textarea
37+
id: reproduce
38+
attributes:
39+
label: Reproduction steps
40+
description: Numbered steps. The shorter the better.
41+
placeholder: |
42+
1. Open the catalog
43+
2. Tap a book
44+
3. ...
45+
validations:
46+
required: true
47+
- type: textarea
48+
id: logs
49+
attributes:
50+
label: Logs
51+
description: |
52+
Android: `adb logcat -d -s Quire:* AndroidRuntime:E`.
53+
Server: `kubectl logs` or `docker logs` output around the failure.
54+
Strip anything sensitive.
55+
render: shell
56+
- type: input
57+
id: device
58+
attributes:
59+
label: Device / OS (Android only)
60+
placeholder: Boox Note Air 3, Android 12
61+
- type: input
62+
id: calibre-web
63+
attributes:
64+
label: calibre-web version
65+
placeholder: 0.6.21

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
blank_issues_enabled: false
2+
contact_links:
3+
- name: Security vulnerability
4+
url: https://github.com/vitofico/opds-ereader-android-app/security/advisories/new
5+
about: Use a private security advisory, not a public issue.
6+
- name: calibre-web bug
7+
url: https://github.com/janeczku/calibre-web/issues
8+
about: Report bugs in calibre-web itself upstream.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Feature request
2+
description: Suggest something this project should do.
3+
labels: ["enhancement"]
4+
body:
5+
- type: markdown
6+
attributes:
7+
value: |
8+
Quire's scope is deliberately narrow (see CONTRIBUTING.md).
9+
Requests that don't fit will be closed politely; please don't
10+
take it personally.
11+
- type: textarea
12+
id: problem
13+
attributes:
14+
label: What problem does this solve?
15+
description: Describe the user-facing problem, not the proposed solution.
16+
validations:
17+
required: true
18+
- type: textarea
19+
id: proposal
20+
attributes:
21+
label: Proposed solution
22+
validations:
23+
required: true
24+
- type: textarea
25+
id: alternatives
26+
attributes:
27+
label: Alternatives considered
28+
- type: dropdown
29+
id: scope
30+
attributes:
31+
label: Does this fit Quire's scope?
32+
description: |
33+
See CONTRIBUTING.md for what's in scope. If unsure, pick "Unsure".
34+
options:
35+
- Yes — self-hosted, no telemetry, calibre-web-centric
36+
- Unsure
37+
- Maybe a fork is better
38+
validations:
39+
required: true

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
## Summary
2+
3+
<!-- 1–3 sentences on what this PR changes and why. -->
4+
5+
## Component(s)
6+
7+
- [ ] Android app
8+
- [ ] Sync server
9+
- [ ] Documentation
10+
- [ ] CI / build
11+
12+
## Checklist
13+
14+
- [ ] Tests added or updated (or N/A — explain)
15+
- [ ] Docs updated in the same PR (or N/A)
16+
- [ ] If Android UI changed: screenshots attached
17+
- [ ] If server schema changed: Alembic migration included
18+
- [ ] If `core/identity` changed: matching change on the other side + fixtures updated
19+
- [ ] No new third-party SDKs / analytics / telemetry
20+
- [ ] Commit messages follow gitmoji + conventional commits
21+
22+
## Related issues
23+
24+
<!-- Closes #N, refs #M, etc. -->

.github/workflows/android-ci.yaml

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,30 @@ on:
2828
- "gradlew"
2929
- ".github/workflows/android-ci.yaml"
3030

31+
# Cancel obsolete runs when new commits land on a PR / branch. Never
32+
# cancel runs on main (we want every commit's tag) or on tag refs.
33+
concurrency:
34+
group: android-ci-${{ github.ref }}
35+
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') }}
36+
3137
jobs:
3238
build:
39+
# Skip on tag pushes — the release job below handles them and the
40+
# commit was already built when it landed on main.
41+
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
3342
runs-on: ubuntu-latest
3443
permissions:
3544
contents: write
3645
steps:
37-
- uses: actions/checkout@v4
38-
- uses: actions/setup-java@v4
46+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
47+
- uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
3948
with:
4049
distribution: temurin
4150
java-version: "17"
42-
- uses: android-actions/setup-android@v3
51+
- uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
4352
with:
4453
packages: "platform-tools platforms;android-34 build-tools;34.0.0"
45-
- uses: gradle/actions/setup-gradle@v4
54+
- uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4
4655
- name: Pin build date
4756
run: echo "BUILD_DATE=$(date -u +%Y-%m-%d)" >> "$GITHUB_ENV"
4857
- name: Compute version
@@ -58,7 +67,7 @@ jobs:
5867
run: ./gradlew lint --stacktrace
5968
- name: Upload debug APK
6069
if: github.ref == 'refs/heads/main'
61-
uses: actions/upload-artifact@v4
70+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
6271
with:
6372
name: app-debug-${{ env.VERSION_NAME }}
6473
path: app/build/outputs/apk/debug/*.apk
@@ -70,3 +79,40 @@ jobs:
7079
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
7180
git tag "v${VERSION_NAME}"
7281
git push origin "v${VERSION_NAME}"
82+
83+
# Tag-triggered: builds a signed release APK and attaches it to a
84+
# GitHub Release. Tags come from the build job's "Tag release" step on
85+
# main. We do not depend on `build` because that job is skipped on tag
86+
# pushes (the same commit was already tested when it landed on main).
87+
release:
88+
runs-on: ubuntu-latest
89+
if: startsWith(github.ref, 'refs/tags/v')
90+
permissions:
91+
contents: write
92+
steps:
93+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
94+
- uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
95+
with:
96+
distribution: temurin
97+
java-version: "17"
98+
- uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
99+
with:
100+
packages: "platform-tools platforms;android-34 build-tools;34.0.0"
101+
- uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4
102+
- name: Decode keystore
103+
if: env.KEYSTORE_B64 != ''
104+
run: echo "$KEYSTORE_B64" | base64 -d > "$RUNNER_TEMP/release.keystore"
105+
env:
106+
KEYSTORE_B64: ${{ secrets.QUIRE_RELEASE_KEYSTORE_B64 }}
107+
- name: Assemble release APK
108+
env:
109+
QUIRE_RELEASE_KEYSTORE: ${{ secrets.QUIRE_RELEASE_KEYSTORE_B64 != '' && format('{0}/release.keystore', runner.temp) || '' }}
110+
QUIRE_RELEASE_KEYSTORE_PASSWORD: ${{ secrets.QUIRE_RELEASE_KEYSTORE_PASSWORD }}
111+
QUIRE_RELEASE_KEY_ALIAS: ${{ secrets.QUIRE_RELEASE_KEY_ALIAS }}
112+
QUIRE_RELEASE_KEY_PASSWORD: ${{ secrets.QUIRE_RELEASE_KEY_PASSWORD }}
113+
run: ./gradlew :app:assembleRelease --stacktrace
114+
- name: Create GitHub Release
115+
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
116+
with:
117+
files: app/build/outputs/apk/release/*.apk
118+
generate_release_notes: true

.github/workflows/codeql.yaml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: codeql
2+
on:
3+
push:
4+
branches: [main]
5+
pull_request:
6+
branches: [main]
7+
schedule:
8+
- cron: "0 6 * * 1"
9+
workflow_dispatch:
10+
11+
# Cancel obsolete runs when a new commit lands on the same ref.
12+
concurrency:
13+
group: codeql-${{ github.ref }}
14+
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
15+
16+
jobs:
17+
analyze:
18+
# CodeQL upload requires Code Scanning enabled, which is free on
19+
# public repos and gated behind GitHub Advanced Security on private
20+
# ones. Skip the whole workflow until the repo is public.
21+
if: github.event.repository.visibility == 'public'
22+
runs-on: ubuntu-latest
23+
permissions:
24+
security-events: write
25+
actions: read
26+
contents: read
27+
strategy:
28+
fail-fast: false
29+
matrix:
30+
language: [java-kotlin, python]
31+
steps:
32+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
33+
- name: Initialize CodeQL
34+
uses: github/codeql-action/init@0daab03d71ff584ef619d027a3fd9146679c5d84 # v3
35+
with:
36+
languages: ${{ matrix.language }}
37+
- if: matrix.language == 'java-kotlin'
38+
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
39+
with:
40+
distribution: temurin
41+
java-version: "17"
42+
- if: matrix.language == 'java-kotlin'
43+
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
44+
with:
45+
packages: "platform-tools platforms;android-34 build-tools;34.0.0"
46+
- if: matrix.language == 'java-kotlin'
47+
run: ./gradlew assembleDebug -x test --stacktrace
48+
- if: matrix.language == 'python'
49+
uses: github/codeql-action/autobuild@0daab03d71ff584ef619d027a3fd9146679c5d84 # v3
50+
- uses: github/codeql-action/analyze@0daab03d71ff584ef619d027a3fd9146679c5d84 # v3

.github/workflows/server-ci.yaml

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@ on:
1212
- ".github/workflows/server-ci.yaml"
1313
- "core/identity/src/test/resources/identity/**"
1414

15+
# Cancel superseded runs on the same branch / PR.
16+
concurrency:
17+
group: server-ci-${{ github.ref }}
18+
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
19+
1520
jobs:
1621
test:
1722
runs-on: ubuntu-latest
1823
defaults:
1924
run:
2025
working-directory: server
2126
steps:
22-
- uses: actions/checkout@v4
23-
- uses: astral-sh/setup-uv@v3
27+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
28+
- uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
2429
- name: Set up Python
2530
run: uv python install 3.12
2631
- name: Install
@@ -40,17 +45,45 @@ jobs:
4045
contents: read
4146
packages: write
4247
steps:
43-
- uses: actions/checkout@v4
44-
- uses: docker/setup-buildx-action@v3
45-
- uses: docker/login-action@v3
48+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
49+
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
50+
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
4651
with:
4752
registry: ghcr.io
4853
username: ${{ github.actor }}
4954
password: ${{ secrets.GITHUB_TOKEN }}
50-
- uses: docker/build-push-action@v6
55+
- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
5156
with:
5257
context: server
5358
push: true
59+
sbom: true
60+
provenance: mode=max
5461
tags: |
5562
ghcr.io/${{ github.repository_owner }}/opds-sync:${{ github.sha }}
5663
ghcr.io/${{ github.repository_owner }}/opds-sync:latest
64+
65+
scan:
66+
needs: image
67+
runs-on: ubuntu-latest
68+
if: github.ref == 'refs/heads/main'
69+
permissions:
70+
contents: read
71+
actions: read
72+
packages: read
73+
security-events: write
74+
steps:
75+
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
76+
with:
77+
registry: ghcr.io
78+
username: ${{ github.actor }}
79+
password: ${{ secrets.GITHUB_TOKEN }}
80+
- uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
81+
with:
82+
image-ref: ghcr.io/${{ github.repository_owner }}/opds-sync:${{ github.sha }}
83+
format: sarif
84+
output: trivy.sarif
85+
severity: HIGH,CRITICAL
86+
ignore-unfixed: true
87+
- uses: github/codeql-action/upload-sarif@0daab03d71ff584ef619d027a3fd9146679c5d84 # v3
88+
with:
89+
sarif_file: trivy.sarif

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,13 @@ local.properties
77
captures/
88
.cxx/
99
.superpowers/
10+
11+
# Secrets — never commit. The release keystore lives outside the repo.
12+
*.keystore
13+
*.jks
14+
*.p12
15+
*.b64
16+
.env
17+
18+
# Claude Code session state
19+
.claude/

0 commit comments

Comments
 (0)