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
14 changes: 7 additions & 7 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,33 @@ updates:
directory: /
schedule:
interval: monthly
time: "08:00"
time: '08:00'
timezone: Asia/Tokyo
cooldown:
default-days: 3
semver-major-days: 7
ignore:
- dependency-name: "@types/node"
- dependency-name: '@types/node'
update-types:
- "version-update:semver-major"
- 'version-update:semver-major'
groups:
actions-packages:
patterns:
- "@actions/*"
- '@actions/*'
dev-dependencies:
dependency-type: development
exclude-patterns:
- "@biomejs/biome"
- '@biomejs/biome'

- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
time: "08:00"
time: '08:00'
timezone: Asia/Tokyo
cooldown:
default-days: 3
groups:
github-actions-official:
patterns:
- "actions/*"
- 'actions/*'
20 changes: 10 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# AGENTS.md

* **`dist/` is committed**: GitHub Actions loads `dist/` directly from the tagged commit. Run `npm run package` and commit the result after any source change.
* **`action.yml` is the source of truth for node version**: `build.mjs` parses it to set the esbuild target. Do not hardcode node versions elsewhere.
* **SHA-256 single-pass**: Download and hash use one `pipeline()` pass. Do not split into separate steps.
* **Retry wraps download + verify**: `downloadAndVerify` retries the entire `downloadWithHash` + SHA-256 check as a unit. Do not retry at the HTTP layer alone — a corrupted transfer must re-download from scratch.
* **Timeouts use native mechanisms**: HTTP uses `HttpClient`'s `socketTimeout` (idle detection). Git uses `GIT_HTTP_LOW_SPEED_LIMIT`/`GIT_HTTP_LOW_SPEED_TIME` env vars (git-native idle detection) plus `execWithTimeout` (total command timeout). Do not replace with custom polling.
* **Cache keys include OS**: Both SDK and pub cache keys include the OS name for easier cache management in the GitHub Actions UI, even though `@actions/cache` internally scopes by runner OS.
* **ESM mocking**: Use `vi.mock()` + `Mocked<typeof>`, not `vi.spyOn()` on module namespaces. A class passed to `mockImplementation()` must define its members as class fields (`get = vi.fn()...`); prototype methods on it are not reachable from the created instance. Automocked classes (bare `vi.mock()`) do expose prototype methods as mocks.
* **Test state is reset before every test**: `vitest.config.mts` sets `mockReset`, `restoreMocks` and `unstubEnvs`, so mock implementations, `vi.spyOn` spies and `vi.stubEnv` values never carry over; do not add manual cleanup for them. Give a `vi.mock` factory's default behaviour as `vi.fn(impl)`; a `vi.fn().mockResolvedValue(...)` chain does not survive the reset. Fake timers have no such option: wrap `vi.useFakeTimers()` in `try/finally` with `vi.useRealTimers()`.
* **Timeout tests require handler-first ordering**: When testing `execWithTimeout` with fake timers, set up the rejection handler (`expect(promise).rejects`) before advancing timers to avoid unhandled rejections.
* **Test coverage must stay at 100%**: Statements, branches, functions, and lines are all 100%. Run `npx vitest run --coverage` to verify after any change.
- **`dist/` is committed**: GitHub Actions loads `dist/` directly from the tagged commit. Run `npm run package` and commit the result after any source change.
- **`action.yml` is the source of truth for node version**: `build.mjs` parses it to set the esbuild target. Do not hardcode node versions elsewhere.
- **SHA-256 single-pass**: Download and hash use one `pipeline()` pass. Do not split into separate steps.
- **Retry wraps download + verify**: `downloadAndVerify` retries the entire `downloadWithHash` + SHA-256 check as a unit. Do not retry at the HTTP layer alone — a corrupted transfer must re-download from scratch.
- **Timeouts use native mechanisms**: HTTP uses `HttpClient`'s `socketTimeout` (idle detection). Git uses `GIT_HTTP_LOW_SPEED_LIMIT`/`GIT_HTTP_LOW_SPEED_TIME` env vars (git-native idle detection) plus `execWithTimeout` (total command timeout). Do not replace with custom polling.
- **Cache keys include OS**: Both SDK and pub cache keys include the OS name for easier cache management in the GitHub Actions UI, even though `@actions/cache` internally scopes by runner OS.
- **ESM mocking**: Use `vi.mock()` + `Mocked<typeof>`, not `vi.spyOn()` on module namespaces. A class passed to `mockImplementation()` must define its members as class fields (`get = vi.fn()...`); prototype methods on it are not reachable from the created instance. Automocked classes (bare `vi.mock()`) do expose prototype methods as mocks.
- **Test state is reset before every test**: `vitest.config.mts` sets `mockReset`, `restoreMocks` and `unstubEnvs`, so mock implementations, `vi.spyOn` spies and `vi.stubEnv` values never carry over; do not add manual cleanup for them. Give a `vi.mock` factory's default behaviour as `vi.fn(impl)`; a `vi.fn().mockResolvedValue(...)` chain does not survive the reset. Fake timers have no such option: wrap `vi.useFakeTimers()` in `try/finally` with `vi.useRealTimers()`.
- **Timeout tests require handler-first ordering**: When testing `execWithTimeout` with fake timers, set up the rejection handler (`expect(promise).rejects`) before advancing timers to avoid unhandled rejections.
- **Test coverage must stay at 100%**: Statements, branches, functions, and lines are all 100%. Run `npx vitest run --coverage` to verify after any change.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

Set up Flutter SDK for GitHub Actions.

* **Pure TypeScript** — No external tools. Consistent behavior across Linux, macOS, and Windows.
* **SHA-256 verification** — Every downloaded archive is verified against the official manifest to detect tampering.
* **Reliable downloads** — Automatic retry (3 attempts with jitter) and socket/idle timeouts for both HTTP downloads and git operations.
* **Automated maintenance** — Dependencies are kept up to date via Dependabot with auto-merge enabled for patch and minor updates.
- **Pure TypeScript** — No external tools. Consistent behavior across Linux, macOS, and Windows.
- **SHA-256 verification** — Every downloaded archive is verified against the official manifest to detect tampering.
- **Reliable downloads** — Automatic retry (3 attempts with jitter) and socket/idle timeouts for both HTTP downloads and git operations.
- **Automated maintenance** — Dependencies are kept up to date via Dependabot with auto-merge enabled for patch and minor updates.

## Usage

Expand Down Expand Up @@ -199,11 +199,11 @@ The action sets the following outputs:

The intro bullets above are backed by a consistent design: every artifact this action installs is verified, and no input ever reaches a shell.

* **Archive verification** — Release archives are downloaded over HTTPS and verified against the SHA-256 checksum in the official release manifest before extraction. A mismatch fails the run.
* **Git mode verification** — Versions and refs are resolved to a commit hash first (via the release manifest or `git ls-remote`), and after cloning the checkout is verified to match that commit. SDK caches are keyed by the commit hash and the repository URL.
* **No shell interpolation** — Inputs and version-file contents are passed to subprocesses as argument arrays. Nothing is ever interpolated into a shell command.
* **Supply chain** — All runtime dependencies are bundled into `dist/`, and CI verifies the bundle is reproducible from source on every change. The repository's own workflows are SHA-pinned, run with minimal permissions, and use `npm ci --ignore-scripts` wherever a write token is present.
* **Cache integrity** — Caches are restored by exact key match only. There is no partial-match fallback that could restore unexpected content.
- **Archive verification** — Release archives are downloaded over HTTPS and verified against the SHA-256 checksum in the official release manifest before extraction. A mismatch fails the run.
- **Git mode verification** — Versions and refs are resolved to a commit hash first (via the release manifest or `git ls-remote`), and after cloning the checkout is verified to match that commit. SDK caches are keyed by the commit hash and the repository URL.
- **No shell interpolation** — Inputs and version-file contents are passed to subprocesses as argument arrays. Nothing is ever interpolated into a shell command.
- **Supply chain** — All runtime dependencies are bundled into `dist/`, and CI verifies the bundle is reproducible from source on every change. The repository's own workflows are SHA-pinned, run with minimal permissions, and use `npm ci --ignore-scripts` wherever a write token is present.
- **Cache integrity** — Caches are restored by exact key match only. There is no partial-match fallback that could restore unexpected content.

## Blog Post

Expand Down
11 changes: 9 additions & 2 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.5.14/schema.json",
"$schema": "https://biomejs.dev/schemas/2.6.4/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
Expand All @@ -10,7 +10,9 @@
"**",
"!!**/dist",
"!.claude",
"!__tests__/fixtures/*-invalid.*"
"!__tests__/fixtures/*-invalid.*",
"!__tests__/fixtures/**/*.yaml",
"!__tests__/fixtures/**/*.yml"
]
},
"formatter": {
Expand All @@ -28,6 +30,11 @@
"quoteStyle": "double"
}
},
"yaml": {
"formatter": {
"quoteStyle": "single"
}
},
"assist": {
"enabled": true,
"actions": {
Expand Down
108 changes: 72 additions & 36 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"semver": "^7.8.5"
},
"devDependencies": {
"@biomejs/biome": "2.5.14",
"@biomejs/biome": "2.6.4",
"@types/node": "^26.6.6",
"@types/semver": "^7.8.0",
"@vitest/coverage-v8": "^5.0.5",
Expand Down
Loading