Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .firebaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projects": {
"default": "spectrumpit"
}
}
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @Project516
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
ci:
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Install Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: 3.44.4
cache: true
Comment on lines +21 to +28

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-remote --refs https://github.com/actions/checkout.git refs/tags/v7
git ls-remote --refs https://github.com/subosito/flutter-action.git refs/tags/v2

Repository: Spectrum3847/spectrum-pit

Length of output: 271


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- workflow ---'
cat -n .github/workflows/ci.yml

printf '%s\n' '--- checkout tag resolution ---'
git ls-remote https://github.com/actions/checkout.git \
  refs/tags/v7 'refs/tags/v7^{}'

printf '%s\n' '--- flutter-action tag resolution ---'
git ls-remote https://github.com/subosito/flutter-action.git \
  refs/tags/v2 'refs/tags/v2^{}'

Repository: Spectrum3847/spectrum-pit

Length of output: 1351


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for spec in \
  "actions/checkout 3d3c42e5aac5ba805825da76410c181273ba90b1" \
  "subosito/flutter-action 1a449444c387b1966244ae4d4f8c696479add0b2"
do
  repo="${spec% *}"
  sha="${spec#* }"
  printf '%s\n' "--- $repo@$sha ---"
  if command -v gh >/dev/null 2>&1; then
    gh api "repos/$repo/commits/$sha" --jq '{sha: .sha, message: .commit.message}'
  else
    curl --fail --silent --show-error \
      -H 'Accept: application/vnd.github+json' \
      "https://api.github.com/repos/$repo/commits/$sha" |
      jq '{sha: .sha, message: .commit.message}'
  fi
done

Repository: Spectrum3847/spectrum-pit

Length of output: 1075


Pin both actions to immutable commits and disable checkout credentials.

Use actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 and subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2. Because no later step requires Git authentication, set persist-credentials: false for checkout.

🧰 Tools
🪛 zizmor (1.28.0)

[warning] 21-22: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 25-25: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 21 - 28, Update the Checkout step to
use the immutable actions/checkout commit
3d3c42e5aac5ba805825da76410c181273ba90b1 and set persist-credentials to false.
Update the Install Flutter step to use the immutable subosito/flutter-action
commit 1a449444c387b1966244ae4d4f8c696479add0b2, preserving its existing Flutter
version and cache settings.

Source: Linters/SAST tools


- name: Fetch packages
run: flutter pub get

- name: Verify formatting
run: dart format --output=none --set-exit-if-changed .

- name: Analyze
run: flutter analyze --fatal-infos

- name: Run tests
run: flutter test
99 changes: 99 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

# TBA API key — never commit
.env

# Firebase — credentials and generated configs
.firebase/
firebase-debug.log
firestore-debug.log
**/google-services.json
**/GoogleService-Info.plist
**/serviceAccount*.json

# claude
.claude/settings.local.json

# flutter setup

temp/

# Agent session-log dumps (sometimes accidentally placed at repo root by
# Claude Code workflows; never commit them).
claude-context-*/
/log.txt

# node
versions/node
alias/
.cache/bin/node*

# impeccable critique snapshots (generated archives; the design.json sidecar
# and DESIGN.md/PRODUCT.md are tracked, but per-run critiques are not).
.impeccable/critique/
# accuracy-cron dependencies (lockfile is tracked, modules are not)
**/.pnpm-store/
scripts/accuracy-cron/node_modules/
scripts/sheets-export-cron/node_modules/

# Background-agent git worktrees (transient)
.claude/worktrees/
scripts/promotion-cron/node_modules/
scripts/report-cron/node_modules/

# mimocode agent-tooling state (never ship or track; see #533)
.mimocode/
exports/

# Cloudflare Worker local state (scripts/photo-worker)
.wrangler/

# misc
pit-app-device-test-plan.html
pit-1.0.0-maintainer-checklist.html
spectrumpit-setup.html
prompt.txt
45 changes: 45 additions & 0 deletions .metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "cc0734ac716fbb8b90f3f9db8020958b1553afa7"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
- platform: android
create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
- platform: ios
create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
- platform: linux
create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
- platform: macos
create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
- platform: web
create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
- platform: windows
create_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7
base_revision: cc0734ac716fbb8b90f3f9db8020958b1553afa7

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
28 changes: 28 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Contributing to Spectrum Pit

Thanks for your interest. This repository is a release mirror, which changes the mechanics a little.

## How changes flow

- The team develops in a private repository. Every published release is synced here as one squashed commit.
- Pull requests here are reviewed by the maintainer. When accepted, the PR is merged here and the maintainer opens a matching PR in the internal repository, so the change ships in the next release and survives the next sync.

## Before you open a PR

- Toolchain: Flutter 3.44.4 / Dart 3.11.5.
- Run the same gates CI runs:

```bash
flutter pub get
dart format --output=none --set-exit-if-changed .
flutter analyze --fatal-infos
flutter test
```

- Keep changes small and focused, one concern per PR.
- No emojis anywhere: code, comments, docs, commit messages, or UI strings. Use plain text, or Material icons in UI.
- UI changes should keep the existing visual system: the palette tokens in `lib/src/theme/pit_palette.dart`, IBM Plex type, flat surfaces, and 8px corners for standard components (12px for sheets and dialogs).

## License

By contributing you agree that your contribution is licensed under AGPL-3.0, the same license as the project.
28 changes: 28 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
16 changes: 16 additions & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/

# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

.kotlin/
74 changes: 74 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import com.flutter.gradle.FlutterExtension
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
import java.io.FileInputStream
import java.util.Properties

plugins {
id("com.android.application")
id("dev.flutter.flutter-gradle-plugin")
// START: FlutterFire Configuration
id("com.google.gms.google-services")
// END: FlutterFire Configuration
}

val flutterExtension = extensions.getByType<FlutterExtension>()

val keystorePropertiesFile = rootProject.file("key.properties")
val keystoreProperties = Properties().apply {
if (keystorePropertiesFile.exists()) {
load(FileInputStream(keystorePropertiesFile))
}
}
val hasReleaseSigning = keystorePropertiesFile.exists()

android {
namespace = "org.spectrum3847.spectrumpit"
compileSdk = flutterExtension.compileSdkVersion
ndkVersion = flutterExtension.ndkVersion

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

defaultConfig {
applicationId = "org.spectrum3847.spectrumpit"
minSdk = flutterExtension.minSdkVersion
targetSdk = flutterExtension.targetSdkVersion
versionCode = flutterExtension.versionCode
versionName = flutterExtension.versionName
}

signingConfigs {
if (hasReleaseSigning) {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = file(keystoreProperties["storeFile"] as String)
storePassword = keystoreProperties["storePassword"] as String
}
}
}

buildTypes {
release {
// Release builds use the keystore declared in android/key.properties
// (gitignored). When that file is absent (e.g. CI without secrets
// wired up), fall back to the debug keystore so the build still
// produces an installable APK -- the artifact just won't match the
// release SHA-1 registered in Firebase.
signingConfig = if (hasReleaseSigning) {
signingConfigs.getByName("release")
} else {
signingConfigs.getByName("debug")
}
}
}
Comment on lines +54 to +67

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not sign release builds with the debug keystore.

The debug keystore uses a publicly known key and password. An APK signed with it can be forged by anyone, and it can never be upgraded to a real release-signed build on the same device. If key.properties is absent, leave the release build unsigned instead. An unsigned artifact fails loudly and cannot be mistaken for a shippable build.

🔒 Proposed change
     buildTypes {
         release {
             // Release builds use the keystore declared in android/key.properties
-            // (gitignored). When that file is absent (e.g. CI without secrets
-            // wired up), fall back to the debug keystore so the build still
-            // produces an installable APK -- the artifact just won't match the
-            // release SHA-1 registered in Firebase.
-            signingConfig = if (hasReleaseSigning) {
-                signingConfigs.getByName("release")
-            } else {
-                signingConfigs.getByName("debug")
-            }
+            // (gitignored). When that file is absent (e.g. CI without secrets
+            // wired up), the build stays unsigned so a debug-signed artifact is
+            // never mistaken for a release build.
+            signingConfig = if (hasReleaseSigning) {
+                signingConfigs.getByName("release")
+            } else {
+                null
+            }
         }
     }

If CI must produce an installable artifact without secrets, build the debug variant in CI instead.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
buildTypes {
release {
// Release builds use the keystore declared in android/key.properties
// (gitignored). When that file is absent (e.g. CI without secrets
// wired up), fall back to the debug keystore so the build still
// produces an installable APK -- the artifact just won't match the
// release SHA-1 registered in Firebase.
signingConfig = if (hasReleaseSigning) {
signingConfigs.getByName("release")
} else {
signingConfigs.getByName("debug")
}
}
}
buildTypes {
release {
// Release builds use the keystore declared in android/key.properties
// (gitignored). When that file is absent (e.g. CI without secrets
// wired up), the build stays unsigned so a debug-signed artifact is
// never mistaken for a release build.
signingConfig = if (hasReleaseSigning) {
signingConfigs.getByName("release")
} else {
null
}
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/app/build.gradle.kts` around lines 54 - 67, Update the release
signing configuration in buildTypes.release so that missing hasReleaseSigning
leaves the release build unsigned instead of selecting
signingConfigs.getByName("debug"). Preserve the release
signingConfigs.getByName("release") path when credentials are available, and
require CI to use the debug variant separately when an installable unsigned-free
artifact is needed.

}

tasks.withType<KotlinJvmCompile>().configureEach { compilerOptions.jvmTarget.set(JvmTarget.JVM_17) }

extensions.configure<FlutterExtension>("flutter") {
source = "../.."
}
7 changes: 7 additions & 0 deletions android/app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
Loading