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
33 changes: 33 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Keeps the actions in .github/workflows current. Actions only: pub and the
# Swift packages are pinned by lockfiles a person moves on purpose, after
# reading what changed — pubspec.yaml explains why several pins must not drift.
#
# Every `uses:` is a full commit SHA with the release in a trailing comment, so
# a moved or re-pointed tag changes nothing here until a pull request says so;
# Dependabot rewrites both the SHA and the comment.
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
# A release is not proposed until it has been public for a week. Most
# compromised releases are caught and pulled within days, so this is the
# window in which somebody else finds out first — rather than this
# repository's release job, which holds the store signing keys.
cooldown:
default-days: 7
# One pull request a week rather than one per action: they are reviewed
# together, and CI runs once for all of them.
groups:
github-actions:
patterns: ["*"]
# The summary in commit.md's format (`ci: bump …`). The commit as a whole
# still fails tool/check/commits.sh, on purpose: Dependabot always appends
# `Signed-off-by: dependabot[bot]`, and a commit here is authored by a
# person. Its pull request is a proposal — whoever reviews it re-commits
# it as their own (`git commit --amend --reset-author`, trailer removed)
# and force-pushes, which also makes the person who read the diff the one
# on record for it.
commit-message:
prefix: ci
22 changes: 14 additions & 8 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ jobs:
android: ${{ steps.filter.outputs.android }}
release: ${{ steps.release.outputs.builds }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
# Nothing here pushes or fetches, so the job token is not left in
# .git/config for the steps after this one.
persist-credentials: false
# A push reads its own commit messages below: full history, without
# the file contents nothing here looks at.
fetch-depth: ${{ github.event_name == 'push' && '0' || '1' }}
filter: ${{ github.event_name == 'push' && 'blob:none' || '' }}
- uses: dorny/paths-filter@v4
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: filter
with:
filters: |
Expand Down Expand Up @@ -63,10 +66,13 @@ jobs:
contents: read

steps:
- uses: actions/checkout@v5
# Nothing here pushes, so the job token is not left in .git/config.
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
persist-credentials: false

- name: Setup Java
uses: actions/setup-java@v6
uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0
with:
distribution: "temurin"
java-version: "25"
Expand All @@ -78,18 +84,18 @@ jobs:
# built against an SDK nobody chose, and nothing in the build log would
# say so. See AGENTS.md → Toolchain.
- name: Install toolchain (mise)
uses: jdx/mise-action@v4
uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0

- name: Cache Android NDK
uses: actions/cache@v5
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
with:
path: |
/usr/local/lib/android/sdk/ndk/27.0.12077973
/usr/local/lib/android/sdk/ndk/28.2.13676358
key: ${{ runner.os }}-ndk-27-28

- name: Cache Android sdk
uses: actions/cache@v5
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
env:
cache-name: cache-Android-sdk
with:
Expand Down Expand Up @@ -123,7 +129,7 @@ jobs:
run: bash tool/dev/build.sh android

- name: Upload Artifacts
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: DPIP-Android-Release
path: build/app/outputs/flutter-apk/*.apk
Expand Down
33 changes: 22 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ jobs:
steps:
# Full history: the commit gate walks the range this push or PR adds, and
# a shallow clone has no base to compare against.
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
fetch-depth: 0
# Nothing here pushes, and every step after this one runs code the
# branch under test controls: the job token is not left in
# .git/config for it. The `git fetch`es below are anonymous, which a
# public repository allows.
persist-credentials: false

# The commit gate first, because it is the only failure that cannot be
# fixed by another commit. A message is immutable once pushed, so the
Expand Down Expand Up @@ -95,15 +100,16 @@ jobs:
echo "branch gate: rebased on $BASE_REF, no merge commits"

# The workflows themselves, because a broken one does not fail — it is
# *refused*. GitHub interpolates a `run` block whole before bash sees it,
# so an expression error anywhere in it, comment included, kills the run
# before a job exists: no failing step, and the file path where the name
# should be. Nothing in this repository could have caught that, and it
# took CI down on main for a day.
# *refused*: an expression error kills the run before a job exists. The
# script says more, and pins and checksums the actionlint it runs.
- name: Workflow gate
run: |
bash <(curl -sSf https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) >/dev/null
./actionlint -shellcheck= .github/workflows/*.yml
run: bash tool/check/workflows.sh

# And what they let somebody else do: a moved tag, a token left on disk,
# a job with more permission than it uses, a value pasted into a script.
# The script says more, and pins and checksums the zizmor it runs.
- name: Workflow security gate
run: bash tool/check/workflow_security.sh

# Architecture gate — pure grep, no toolchain, fails fast.
- name: Layering gate
Expand All @@ -119,6 +125,11 @@ jobs:
- name: Storage gate
run: bash tool/check/storage.sh

# Wall-clock gate — every DateTime.now() in lib/ says why the device
# clock is the right one. See ARCHITECTURE.md → Calibrated time.
- name: Wall-clock gate
run: bash tool/check/wall_clock.sh

# Every channel sound must resolve, or the channel is rejected on device.
- name: Notification-sound gate
run: bash tool/check/notification_sounds.sh
Expand All @@ -138,7 +149,7 @@ jobs:
# developers run locally — one source of truth. v4 = Node 24 runtime
# (Node 20 actions are forced onto 24 and emit deprecation warnings).
- name: Install toolchain (mise)
uses: jdx/mise-action@v4
uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0

- name: Install dependencies
run: bash tool/dev/deps.sh
Expand Down Expand Up @@ -199,7 +210,7 @@ jobs:
# tokenless opt-out for public repositories, in the Codecov org settings.
- name: Upload coverage to Codecov
if: ${{ !cancelled() && hashFiles('coverage/lcov.info') != '' }}
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5.5.5
with:
use_oidc: true
files: coverage/lcov.info
Expand Down
18 changes: 12 additions & 6 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ jobs:
ios: ${{ steps.filter.outputs.ios }}
release: ${{ steps.release.outputs.builds }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
# Nothing here pushes or fetches, so the job token is not left in
# .git/config for the steps after this one.
persist-credentials: false
# A push reads its own commit messages below: full history, without
# the file contents nothing here looks at.
fetch-depth: ${{ github.event_name == 'push' && '0' || '1' }}
filter: ${{ github.event_name == 'push' && 'blob:none' || '' }}
- uses: dorny/paths-filter@v4
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: filter
with:
filters: |
Expand Down Expand Up @@ -75,20 +78,23 @@ jobs:
contents: read

steps:
- uses: actions/checkout@v5
# Nothing here pushes, so the job token is not left in .git/config.
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
persist-credentials: false

# Flutter and Dart come from mise.toml, the same pin CI and every laptop
# use. Not subosito/flutter-action with `channel: stable`: that resolves
# to whatever stable is on the day, so this job could ship an artifact
# built against an SDK nobody chose, and nothing in the build log would
# say so. See AGENTS.md → Toolchain.
- name: Install toolchain (mise)
uses: jdx/mise-action@v4
uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0

# The iOS build uses Swift Package Manager, not CocoaPods (there is no
# Podfile). Cache SPM's clone/artifact store keyed on the resolved pins.
- name: Cache Swift Packages
uses: actions/cache@v5
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
with:
# build/ios/SourcePackages, not DerivedData. Flutter always passes
# -clonedSourcePackagesDirPath pointing there (ios/xcodeproj.dart), so
Expand Down Expand Up @@ -130,7 +136,7 @@ jobs:
zip -qr DPIP.ipa Payload

- name: Upload Artifacts
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: DPIP-iOS
path: |
Expand Down
Loading
Loading