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: 2 additions & 3 deletions .github/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ GitHub's config for this repo: CI, releases, Pages, dependabot, issue forms, PR

| Path | Purpose |
| --- | --- |
| `workflows/ci.yml` | push-main + PR gate. A `changes` job diffs the base against the head and gates the other jobs by area (`docs checks` always runs); `ci pass` aggregates them |
| `workflows/ci.yml` | push-main + PR gate. A `changes` job diffs the base against the head and gates the other jobs by area (`docs checks` always runs; `site build` runs `scripts/assemble-site.sh` and `tsc` on the docs when the site files change); `ci pass` aggregates them |
| `workflows/release.yml` | on `v*` tag: check the tag against `MARKETING_VERSION` and `CHANGELOG.md`, then (environment `release`) sign, notarize, write the Sparkle appcast, attest, publish. `workflow_dispatch` is a rehearsal that publishes nothing |
| `workflows/go-weekly.yml` | Monday run of the Go checks, so new govulncheck findings surface without a code change, plus `go test -fuzz` for 30 s per `Fuzz*` target (a failing input is uploaded as the `fuzz-failures` artifact); files or updates one `security` issue on failure |
| `workflows/github-pages.yml` | builds `_site` on PR, also deploys on push to main |
| `workflows/github-pages.yml` | on push to main (site paths only): builds `_site` and deploys it. The pre-merge check of the same build is the `site build` job in `ci.yml`; keep its `site` regex and this `paths:` list in step |
| `workflows/reusable-*.yml` | `workflow_call` only. Edit the check here, never in a caller |
| `workflows/scorecard.yml` | OpenSSF Scorecard: Wednesday cron, `branch_protection_rule`, dispatch |
| `dependabot.yml` | github-actions, gomod `/crocmobile`, swift `/app` (reads the Xcode project's `Package.resolved`) weekly; npm `/web/docs` monthly. Minor and patch grouped, majors one PR each |
Expand Down Expand Up @@ -38,5 +38,4 @@ gh api repos/OWNER/REPO/git/ref/tags/TAG --jq '.object.type + " " + .object.sha'
- **A label named in config must already exist** in the repo. Dependabot silently drops missing ones, and a `labels:` filter on `listForRepo` silently matches nothing, which breaks issue dedup.
- **Dependabot bumps only direct Go requirements.** The `tool` block's modules (`x/mobile`, `x/vuln`) are `// indirect`, so bump them by hand: `go get tool` in `crocmobile/`.
- **Go jobs cross-target `darwin/arm64`** because the module only ships through gomobile. Tools have to be built for the runner (`env -u GOOS -u GOARCH go build -o ...`) and then run under the job's env.
- **YAML anchors work** in workflows here (`paths: &site-paths` / `*site-paths` in `github-pages.yml`), despite the common claim that Actions rejects them.
- **`release.yml` parses `CHANGELOG.md`.** It requires a [Keep a Changelog](https://keepachangelog.com/) heading, dated: `## [1.2.3] - YYYY-MM-DD`. Change the heading style and the tag stops releasing. The notes end at the next `## [` heading or at the first line starting `[label]: `, so a reference-style link definition inside a version section cuts its release notes short; use inline links there.
40 changes: 38 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
design: ${{ steps.filter.outputs.design }}
interop: ${{ steps.filter.outputs.interop }}
scripts: ${{ steps.filter.outputs.scripts }}
site: ${{ steps.filter.outputs.site }}

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -65,9 +66,10 @@ jobs:
out swift '^(app|CrocKit)/|^scripts/screenshots/|^\.swift-format$|^\.xcode-version$|^scripts/format-swift\.sh$|^\.github/workflows/(ci|reusable-swift-format)\.yml$'
out go '^crocmobile/|^\.github/workflows/(ci|reusable-go)\.yml$'
out build '^(app|CrocKit|crocmobile)/|^scripts/build-xcframework\.sh$|^\.xcode-version$|^\.github/workflows/ci\.yml$'
out design '^design/|^app/.*\.swift$|^app/CrocApp/Assets\.xcassets/|^web/landing/.*\.(css|html)$|^web/docs/src/css/|^scripts/(lib|verify-design)\.sh$|^\.github/workflows/ci\.yml$'
out design '^design/|^app/.*\.swift$|^app/CrocApp/Assets\.xcassets/|^web/landing/.*\.(css|html|js)$|^web/landing/assets/img/favicon/site\.webmanifest$|^web/docs/src/(css/|prismTheme\.ts$)|^scripts/(lib|verify-design)\.sh$|^\.github/workflows/ci\.yml$'
out interop '^(crocmobile|CrocKit)/|^scripts/(lib|verify-interop)\.sh$|^scripts/croc-v10\.version$|^\.github/workflows/ci\.yml$'
out scripts '^scripts/.*\.sh$|^\.github/workflows/ci\.yml$'
out site '^web/(landing|docs)/|^design/tokens\.css$|^scripts/assemble-site\.sh$|^\.github/workflows/(ci|github-pages)\.yml$'
} | tee -a "$GITHUB_OUTPUT"

swift-format:
Expand Down Expand Up @@ -135,6 +137,40 @@ jobs:

- run: scripts/verify-docs.sh

# The deploy workflow's build, run before merge so a broken docs link or type
# error fails the PR instead of the push to main.
site:
name: site build

needs: changes

if: needs.changes.outputs.site == 'true'

runs-on: ubuntu-26.04

timeout-minutes: 15

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0 # needed because Docusaurus's showLastUpdateTime reads each doc page's git history
persist-credentials: false

- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: web/docs/.nvmrc

# Version from `packageManager`. Not corepack: Node 25+ no longer ships it.
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
package_json_file: web/docs/package.json
cache: true
cache_dependency_path: web/docs/pnpm-lock.yaml

- run: scripts/assemble-site.sh

- run: pnpm --dir web/docs run typecheck

# The interop harness against a relay on localhost, never the public relays:
# those rate-limit a shared runner address and make the run flaky. The
# public-pool scenario stays a local run (docs/BUILDING.md).
Expand Down Expand Up @@ -297,7 +333,7 @@ jobs:
pass:
name: ci pass

needs: [changes, swift-format, go, contrast, shellcheck, docs, interop, build]
needs: [changes, swift-format, go, contrast, shellcheck, docs, site, interop, build]

if: ${{ always() }}

Expand Down
8 changes: 2 additions & 6 deletions .github/workflows/github-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@ name: GitHub Pages
on:
push:
branches: [main]
paths: &site-paths
paths:
- "web/landing/**"
- "web/docs/**"
- "design/tokens.css"
- "scripts/assemble-site.sh"
- ".github/workflows/github-pages.yml"
pull_request:
paths: *site-paths

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
cancel-in-progress: false

jobs:
build:
Expand Down Expand Up @@ -63,8 +61,6 @@ jobs:

needs: build

if: github.event_name == 'push'

runs-on: ubuntu-26.04

timeout-minutes: 10
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ extra sections where a release needs them, and the project follows

### Changed

- Buttons are easier to read in light mode: text labels now use the darker green, and destructive labels are no longer drawn in red text.
- Buttons are easier to read in light mode: text labels and filled buttons now use the darker green, so white labels pass the 4.5:1 contrast mark, and destructive labels are no longer drawn in red text.
- The website and docs are refreshed. The docs are reorganized around what you are doing (one Settings page in the app's own order, Verify a release under Getting started, Send as link and a keyboard shortcut reference), with a new Uninstall and data page, many accuracy fixes, and redirects from the old addresses. The home page leads with "Send files with a code phrase" and shows the iPhone app beside the Mac one.
- The website and docs use the app icon as their favicon and home-screen icon, and a new social preview card.
- The screen that asks you to accept a transfer shows a Receiving header, the encryption badge and the file list as rows. Its Cancel button is gone: Decline answers the other device, leaving the screen gives no answer, and Home shows a banner to return to a request that is still waiting.
- Send as Link now says Sending and Receiving like every other transfer, and copying the code shows "Copied".
- On iPhone, a light haptic marks a finished, failed or incoming transfer and a copied or scanned code. Cancelling or declining yourself stays silent.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<h1 align="center">CrocApp</h1>

<p align="center">
<strong>Send a file to any device, anywhere, with a code phrase.</strong>
<strong>Send files with a code phrase</strong>
<br>
A free, open-source native SwiftUI app for Mac, with iPhone and iPad arriving in the App Store release.
</p>
Expand Down Expand Up @@ -32,7 +32,7 @@ It is a graphical front end for [croc](https://github.com/schollz/croc), the com
| Mac (Apple silicon only) | `CrocApp-<version>-arm64.dmg` from [GitHub Releases](https://github.com/bakirgdev/CrocApp/releases/latest) |
| Mac (Homebrew cask) | Not available yet. Homebrew accepts a cask once the repo shows [enough public interest](https://docs.brew.sh/Package-Acceptance-Policy#notability): 225 stars, 90 watchers, or 90 forks. Starring the repo helps |

The disk image is Developer ID signed, notarized, and updates itself through Sparkle (**Check for Updates…** in the app menu). Verify it with `gh attestation verify <file> --repo bakirgdev/CrocApp` ([`SECURITY.md`](SECURITY.md)). Install guide: [crocapp.dev/docs/getting-started/install](https://crocapp.dev/docs/getting-started/install/). Or [build from source](docs/BUILDING.md).
The disk image is Developer ID signed, notarized, and updates itself through Sparkle (**Check for Updates…** in the app menu). Verify it with `gh attestation verify <file> --repo bakirgdev/CrocApp` ([`SECURITY.md`](SECURITY.md)). Install guide: [crocapp.dev/docs/getting-started/install](https://crocapp.dev/docs/getting-started/install/). To check a download, see [Verify a release](https://crocapp.dev/docs/getting-started/verify-downloads/). Or [build from source](docs/BUILDING.md).

## How it works

Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Also out of scope: reports generated by a scanner with no demonstrated impact, m

## Verifying downloads

Steps to check a release (notarization, build attestation, checksums, update signing) are in the [verify downloads guide](https://crocapp.dev/docs/verify-downloads/). An asset that fails any check, or one that appears anywhere other than this repository's releases page, is worth reporting.
Steps to check a release (notarization, build attestation, checksums, update signing) are in the [verify downloads guide](https://crocapp.dev/docs/getting-started/verify-downloads/). An asset that fails any check, or one that appears anywhere other than this repository's releases page, is worth reporting.

## What CrocApp claims

Expand All @@ -57,4 +57,4 @@ Transfers are end-to-end encrypted, the code phrase authenticates both devices (
> [!WARNING]
> There has been **no formal third-party security audit** of croc, and CrocApp does not claim one. Do not describe CrocApp as audited, certified, or zero-knowledge-verified.

Supporting facts (no accounts, analytics, ads or tracking, history that stays on the device, your own relay, dependency scanning) are on the [security and privacy page](https://crocapp.dev/docs/security-and-privacy/).
Supporting facts (no accounts, analytics, ads or tracking, history that stays on the device, your own relay) are on the [security and privacy page](https://crocapp.dev/docs/security-and-privacy/).
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.333",
"green" : "0.502",
"red" : "0.090"
"blue" : "0.263",
"green" : "0.384",
"red" : "0.067"
}
},
"idiom" : "universal"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.333",
"green" : "0.502",
"red" : "0.090"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.522",
"green" : "0.773",
"red" : "0.176"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
29 changes: 17 additions & 12 deletions app/CrocApp/Support/AutoVerify.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import ServiceManagement
/// --relay-password PW the custom relay's password (croc's default applies without it)
/// --shown-relay default make the trust UI name the public relay whatever --relay says
/// --shown-link-origin ORIGIN make a stored link show and encode ORIGIN (scheme and host, e.g.
/// https://crocapp.dev) instead of the --store-server's; the upload
/// https://getcroc.com) instead of the --store-server's; the upload
/// still goes to --store-server
/// --throttle RATE cap the upload rate for a send, e.g. 4m (croc's --throttleUpload)
/// --transport auto|relay the Connection setting for this run (croc's --transport)
Expand Down Expand Up @@ -183,7 +183,7 @@ enum AutoVerify {
}
controller.startStoreSend(
urls: [URL(fileURLWithPath: args[i + 1])], downloads: downloads,
lifetimeSeconds: 3600)
lifetimeSeconds: 86_400)
await watchStoreSend(controller, docs: docs, resultURL: resultURL)
} else if let i = args.firstIndex(of: "--auto-store-receive"), i + 1 < args.count {
guard let link = URL(string: args[i + 1]) else {
Expand Down Expand Up @@ -367,40 +367,45 @@ enum AutoVerify {
)] = [
(
false, .completed, false, 177_000,
["Quarterly-Report.pdf", "Ridge-Sunrise.jpg", "Meeting-Notes.md"], 3, "4821",
["Quarterly-Report.pdf", "Ridge-Sunrise.jpg", "Meeting-Notes.md"], 3, "cape-…",
12 * 60, false
),
(true, .completed, true, 148, [], 1, "7305", 2 * hour, false),
(true, .completed, true, 148, [], 1, "wiry-…", 2 * hour, false),
(
true, .completed, false, 55_000, ["Design-Assets.zip"], 1,
"crocapp.dev", 5 * hour, true
"getcroc.com", 5 * hour, true
),
(
false, .completed, false, 24_100_000, ["Soundtrack.wav"], 1, "6142", 1.2 * day,
false, .completed, false, 24_100_000, ["Soundtrack.wav"], 1, "junky-…",
1.2 * day,
false
),
(
true, .declined, false, 184_000, ["Contract-Draft.pdf"], 1, "3057", 2 * day,
true, .declined, false, 184_000, ["Contract-Draft.pdf"], 1, "lint-…", 2 * day,
false
),
(
false, .failed, false, 96_000_000, ["Field-Recordings.zip"], 1, "9134", 4 * day,
false, .failed, false, 96_000_000, ["Field-Recordings.zip"], 1, "mild-…",
4 * day,
false
),
(
true, .completed, false, 18_600_000,
["Slides.pdf", "Cover-Art.png", "Speaker-Notes.md", "Demo-Reel.mov"], 4, "2290",
["Slides.pdf", "Cover-Art.png", "Speaker-Notes.md", "Demo-Reel.mov"], 4, "op…",
6 * day, false
),
(
false, .cancelled, false, 412_000_000,
["Raw-Scans.zip"], 1, "8816", 9 * day, false
["Raw-Scans.zip"], 1, "tamer-…", 9 * day, false
),
(
true, .completed, false, 7_800_000, ["Project-Archive.zip"], 1, "5503",
true, .completed, false, 7_800_000, ["Project-Archive.zip"], 1, "fuzzy-…",
21 * day, false
),
(false, .completed, false, 1_200_000, ["Receipts.pdf"], 1, "1948", 35 * day, false),
(
false, .completed, false, 1_200_000, ["Receipts.pdf"], 1, "rope-…", 35 * day,
false
),
]
for sample in samples {
let record = TransferRecord(
Expand Down
7 changes: 6 additions & 1 deletion app/CrocApp/Support/DesignTokens.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,12 @@ extension Color {
/// never use this for a word, see `accentText`.
static var accentFill: Color { .accentColor }

/// Pressed state for custom button styles built on the accent fill.
/// `--color-accent-solid`: the background of a filled prominent button.
/// Darker than `accentFill` in light so `onAccent` reads at 4.94:1
/// (colors.md rule 3); the same green in dark.
static let accentSolid = Color("ColorAccentSolid")

/// Pressed state for custom button styles built on the accent solid.
static let accentPressed = Color("ColorAccentPressed")

/// `--color-accent-tint`: the drag-over and secondary-button fill. Alpha
Expand Down
2 changes: 1 addition & 1 deletion app/CrocApp/Views/ButtonVariants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private struct ButtonVariantStyle: PrimitiveButtonStyle {

switch variant {
case .prominent:
button.buttonStyle(.borderedProminent).tint(Color.accentFill)
button.buttonStyle(.borderedProminent).tint(Color.accentSolid)
case .secondary:
button.buttonStyle(.bordered).tint(Color.accentFill)
case .destructive:
Expand Down
2 changes: 1 addition & 1 deletion app/CrocApp/Views/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct HomeView: View {
.foregroundStyle(Color.onAccent)
.padding(.horizontal, Spacing.space3)
.padding(.vertical, Spacing.space1)
.background(Color.accentFill, in: Capsule())
.background(Color.accentSolid, in: Capsule())
.offset(x: Spacing.space3, y: -Spacing.space3)
.accessibilityLabel(
"^[\(router.pendingSendURLs.count) file](inflect: true) waiting to send"
Expand Down
Loading
Loading