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
34 changes: 34 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Secrets and signing material
.env
.env.*
*.jks
*.keystore
*.p12
*.pfx
*.pem
*.key
secrets/
android/key.properties

# Generated output and dependency metadata
build/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
artifacts/
docker-output/
.clean-check-tmp/

# Version-control metadata
.git/
.gitignore

# Local IDE and machine configuration
.idea/
.vscode/
*.iml
android/local.properties

# Files not needed to build the image
coverage/
.DS_Store
1 change: 1 addition & 0 deletions .flutter-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.44.0
4 changes: 4 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ body:
to the raw photo. This matters a lot for triage.
options:
- Android
- iOS
- Web
- Linux
- macOS
- Windows
- Other (specify below)
validations:
required: true
Expand Down
6 changes: 3 additions & 3 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ body:
attributes:
label: Privacy
description: >-
FOSScanner makes no network calls of its own and never persists
scans/PDFs beyond the current session (see the README). Please
confirm your suggestion doesn't require breaking that.
FOSScanner makes no network calls of its own and keeps in-progress
pages in memory (the OS may cache a shared PDF; see the README).
Please confirm your suggestion doesn't require breaking that.
options:
- label: This doesn't require adding network calls or persistent storage of scanned content
required: true
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ updates:
directory: "/"
schedule:
interval: "weekly"

- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
77 changes: 65 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,86 @@ on:
pull_request:
branches: [main, dev]

permissions:
contents: read

jobs:
analyze-and-test:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4

- uses: subosito/flutter-action@v2
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: stable
flutter-version: '3.44.0'

- run: flutter pub get
- run: flutter analyze
- run: flutter test

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: pubspec-lock
path: pubspec.lock
retention-days: 14

- run: flutter analyze
- run: flutter test
# Tests compile the host (Linux) native assets only. Compile one shipping
# Android ABI as well so Gradle/NDK/native-asset regressions fail before
# a release tag is created.
- run: flutter build apk --debug --target-platform android-arm64
web:
needs: quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
flutter-version: '3.44.0'
- run: flutter build web

linux:
needs: quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
flutter-version: '3.44.0'
- run: sudo apt-get update && sudo apt-get install -y ninja-build libgtk-3-dev
- run: flutter build linux

- uses: actions/upload-artifact@v4
android:
needs: quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
with:
distribution: temurin
java-version: '17'
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
flutter-version: '3.44.0'
- run: flutter build apk --debug --target-platform android-arm64
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: app-debug-arm64
path: build/app/outputs/flutter-apk/app-debug.apk
retention-days: 14

apple:
needs: quality
runs-on: macos-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
flutter-version: '3.44.0'
- run: flutter build ios --no-codesign
- run: flutter build macos

windows:
needs: quality
runs-on: windows-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
flutter-version: '3.44.0'
- run: flutter build windows
13 changes: 4 additions & 9 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,12 @@ jobs:
contents: write
pull-requests: write
steps:
# Default GITHUB_TOKEN is read-only for this repo/org with no
# repo-level override (see release.yml) and release-please needs to
# open PRs and push tags, so it needs the same RELEASE_TOKEN PAT.
- uses: googleapis/release-please-action@v4
# The organization enforces a read-only GITHUB_TOKEN. Keep the
# repository-scoped PAT confined to this publishing action.
- uses: googleapis/release-please-action@8b8fd2cc23b2e18957157a9d923d75aa0c6f6ad5 # v4
with:
token: ${{ secrets.RELEASE_TOKEN }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
# Without this, release-please-action defaults to the repo's
# GitHub-configured default branch — which is `dev` (the PR
# target for day-to-day work), not `main` (the only branch
# this workflow actually triggers on and releases from). Pin
# it explicitly so the two can't silently diverge again.
# The repository default branch is dev, while releases come from main.
target-branch: main
106 changes: 80 additions & 26 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,107 @@ name: Release
on:
push:
tags:
- 'v*'
# Workflow filters use glob syntax; the shell guard below enforces exact SemVer.
- 'v[0-9]*.[0-9]*.[0-9]*'

permissions:
contents: read

jobs:
preflight:
runs-on: ubuntu-latest
outputs:
validated-sha: ${{ steps.validate.outputs.sha }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0

- name: Validate tag, version, and provenance
id: validate
shell: bash
run: |
if [[ ! "$GITHUB_REF_NAME" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
echo "Invalid release tag: $GITHUB_REF_NAME" >&2
exit 1
fi

pubspec_version="$(awk '$1 == "version:" { print $2 }' pubspec.yaml)"
release_version="${pubspec_version%%+*}"
if [[ "$GITHUB_REF_NAME" != "v$release_version" ]]; then
echo "Tag $GITHUB_REF_NAME does not match pubspec.yaml version $pubspec_version" >&2
exit 1
fi

git fetch --no-tags origin main:refs/remotes/origin/main
if ! git merge-base --is-ancestor "$GITHUB_SHA" "origin/main"; then
echo "Release tag commit is not an ancestor of origin/main" >&2
exit 1
fi

validated_sha="$(git rev-parse "${GITHUB_SHA}^{commit}")"
echo "sha=$validated_sha" >> "$GITHUB_OUTPUT"

- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
flutter-version: '3.44.0'

- name: Quality gate
run: |
flutter pub get
flutter analyze
flutter test

build-and-release:
needs: preflight
runs-on: ubuntu-latest
environment: release
permissions:
contents: write
contents: read
id-token: write
attestations: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ needs.preflight.outputs.validated-sha }}

- uses: actions/setup-java@v4
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
distribution: temurin
java-version: '17'
flutter-version: '3.44.0'

- uses: subosito/flutter-action@v2
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
with:
channel: stable
distribution: temurin
java-version: '17'

- name: Restore release keystore
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > android/app/upload-keystore.jks
test -n "$ANDROID_KEYSTORE_BASE64" || { echo "ANDROID_KEYSTORE_BASE64 is required" >&2; exit 1; }
test -n "$ANDROID_KEYSTORE_PASSWORD" || { echo "ANDROID_KEYSTORE_PASSWORD is required" >&2; exit 1; }
printf '%s' "$ANDROID_KEYSTORE_BASE64" | base64 -d > android/app/upload-keystore.jks
cat > android/key.properties <<EOF
storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
keyPassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
storePassword=$ANDROID_KEYSTORE_PASSWORD
keyPassword=$ANDROID_KEYSTORE_PASSWORD
keyAlias=upload
storeFile=upload-keystore.jks
EOF

- run: flutter pub get
- run: flutter build apk --release --split-per-abi

- uses: softprops/action-gh-release@v2
- uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 # v3
with:
subject-path: build/app/outputs/flutter-apk/app-*-release.apk

- uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
files: build/app/outputs/flutter-apk/app-*-release.apk
# No generate_release_notes here: release-please (release-please.yml)
# already created this release with changelog-based notes when it
# tagged; this step just attaches the built APKs to it. Setting
# generate_release_notes would overwrite those curated notes with
# GitHub's raw auto-generated ones.
fail_on_unmatched_files: true
# Release Please already created the changelog-based release notes;
# this action only attaches the built APKs.
env:
# The org enforces read-only default GITHUB_TOKEN permissions
# (Settings > Actions > General > Workflow permissions is locked
# to "Read repository contents and packages permissions" and
# can't be changed at the repo level), so job-level
# `permissions: contents: write` above isn't enough on its own —
# a PAT scoped to this repo is required to actually create the
# release. RELEASE_TOKEN: fine-grained PAT, Contents: Read and
# write, scoped to this repo only.
# The organization enforces a read-only GITHUB_TOKEN, so publishing
# uses a repository-scoped PAT only in this action.
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ CLAUDE.md
.pub/
/build/
/coverage/
/docker-output/
/.clean-check-tmp/

# Symbolication related
app.*.symbols
Expand Down
7 changes: 4 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ add `Co-Authored-By` trailers for AI coding assistants.

## Reporting bugs / requesting features

Use the issue templates — they ask for the platform (Android/web) and
Flutter version, which is usually the first thing needed to reproduce
anything in this codebase given how much of it is native-only.
Use the issue templates — they ask whether the problem occurs on Android,
iOS, web, Linux, macOS, or Windows, plus the Flutter version. That platform
report is usually the first thing needed to reproduce an issue in this
codebase given how much behavior is platform-specific.

## Security

Expand Down
16 changes: 10 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
FROM ghcr.io/cirruslabs/flutter:stable
# Flutter 3.44.0; pin the multi-platform image index, not a mutable tag.
FROM ghcr.io/cirruslabs/flutter@sha256:46691e311715845de03a3ba4753a475476936805b29431b1f00f1816981033f8

# cmake/ninja/build-essential: required to build opencv_dart's native
# (dartcv4) component via Dart's native-assets build hooks during Flutter
# builds.
# Native assets need CMake/Ninja; Linux desktop builds additionally need
# Clang and GTK development headers. The base digest is pinned, while apt
# security package versions intentionally resolve from the archive at build time.
RUN apt-get update && apt-get install -y --no-install-recommends \
cmake \
ninja-build \
build-essential \
clang \
libgtk-3-dev \
&& rm -rf /var/lib/apt/lists/*

# Set the working directory
WORKDIR /app

# Cache package downloads in the image; the bind-mounted project regenerates
# only its lightweight .dart_tool metadata when a container starts.
# Cache package downloads before copying the rest of the project.
COPY pubspec.* ./
RUN flutter pub get

COPY . .

# Safe directory for git
RUN git config --global --add safe.directory /app
RUN git config --global --add safe.directory /sdks/flutter
Loading
Loading