Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
12 changes: 12 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Git for Windows converts line endings on checkout by default, which is fine
# for source but not for a file this repository compares byte for byte. Pinning
# the working tree to LF everywhere keeps a Windows clone identical to a Linux
# one, and keeps the diff of a file edited on either the change that was made.
* text=auto eol=lf

# Captured verbatim from other programs and compared against what we write, so
# nothing may rewrite them -- not the committed bytes, not a checkout. Marking
# them binary is what says that, whatever a clone is configured to do.
*.acf -text
*.bin -text
*.txtpb -text
108 changes: 99 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,38 @@ env:
# A dependency that fails to build is a dependency we should not have.
CARGO_NET_RETRY: 3

# Every job here reads the repository and writes nothing back, so the token that
# reaches them needs nothing more. It matters most when a runner is self-hosted
# and executing pull-request code: a build script from a fork runs with whatever
# the workflow was handed. `persist-credentials: false` on each checkout keeps
# the token out of .git/config on the runner for the same reason; nothing in the
# workspace is a git dependency, so no step needs it to fetch.
permissions:
contents: read

# Where these jobs run. GitHub's own runners are the default because they are
# always there and cost nothing on a public repository, and because a pull
# request from a fork cannot reach our self-hosted ones at all. The self-hosted
# box is faster when it is up, so to send the jobs back to it set the repository
# variable CI_RUNNER to `self-hosted` (Settings > Secrets and variables >
# Actions > Variables); deleting the variable brings them back here. Nothing in
# this file assumes one or the other: every job installs what it needs.
#
# There is no way to fail over automatically. A job addressed to a runner that
# is offline queues rather than failing, and the API that would say whether one
# is online needs administration rights the workflow token does not have.

jobs:
# Per-commit gate. clippy --all-targets type-checks every bin, test and example
# in the workspace, which is most of the value here; `cargo test` runs beside it
# rather than after, since the runners are 32-core and neither job is the
# in the workspace, which is most of the value here; `cargo test` runs beside
# it rather than after, since the jobs run in parallel and neither is the
# bottleneck.
check:
runs-on: self-hosted
runs-on: ${{ vars.CI_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
Expand All @@ -37,16 +60,77 @@ jobs:
# deterministic. They are run by hand against real Steam.
- run: cargo test --workspace --all-features

# The port to Windows is only a port while something checks it. Cross-checking
# from the Linux runners is enough to catch the regression that matters -- a
# `std::os::unix` import creeping back in -- and costs no new runner. `ring`
# compiles C for the target, so this needs the mingw toolchain the same way
# binary-size needs musl-tools.
windows:
name: Windows cross-check
runs-on: ${{ vars.CI_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
Comment thread
beingsuz marked this conversation as resolved.
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
targets: x86_64-pc-windows-gnu
- uses: Swatinem/rust-cache@v2

- name: Install the mingw toolchain
run: sudo apt-get update && sudo apt-get install -y mingw-w64

- run: cargo clippy --workspace --all-targets --target x86_64-pc-windows-gnu -- -D warnings

# clippy --all-targets type-checks but never links. The CLI is what a
# Windows user actually runs, so link it.
- run: cargo build --target x86_64-pc-windows-gnu -p tapline-cli

# The cross-check above type-checks Windows code but never runs it, and the
# token store now calls the Win32 security APIs by hand. Unsafe FFI nobody
# executes is unsafe FFI nobody has checked, so this runs the suite on a real
# Windows machine. GitHub's Windows runners are free on a public repository,
# and this is the only job that cannot fall back to the self-hosted box --
# those are Linux.
windows-native:
name: Windows tests
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2

# Creating a symbolic link on Windows needs a privilege an ordinary
# account does not have, and tapline creates them when a depot asks for
# them. Developer Mode is the supported way to grant it without elevating,
# and without it the symlink tests cannot run at all.
- name: Turn on Developer Mode, for symlinks
run: |
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v AllowDevelopmentWithoutDevLicense /d 1

# Default features, not --all-features: the `keyring` feature is pinned to
# the secret-service backend, which is Linux's. The Linux job covers it.
- run: cargo clippy --workspace --all-targets -- -D warnings
- run: cargo test --workspace

# The dependency floor is a floor: this fails on a new licence, a yanked crate,
# an advisory, or anything from the deny list in deny.toml (openssl, hyper,
# reqwest, prost, a C build).
deny:
runs-on: self-hosted
runs-on: ${{ vars.CI_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
# The official cargo-deny action runs in Docker, and these runners have no
# Docker daemon. This installs the same binary directly.
# The official cargo-deny action runs in Docker, which the self-hosted
# runners have no daemon for. This installs the same binary directly and
# works wherever the job lands.
- uses: taiki-e/install-action@v2
with:
tool: cargo-deny
Expand All @@ -56,9 +140,11 @@ jobs:
# Also proves the static musl build works, which needs a musl-targeting C
# compiler because rustls's `ring` backend compiles C for the target.
binary-size:
runs-on: self-hosted
runs-on: ${{ vars.CI_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-musl
Expand Down Expand Up @@ -113,9 +199,11 @@ jobs:
# The metadata build is what a webapp links: it must not drag in rayon, cap-std,
# clap or serde. Asserted against the dependency graph rather than hoped for.
minimal-graph:
runs-on: self-hosted
runs-on: ${{ vars.CI_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: No heavy crates in the metadata build
Expand All @@ -132,9 +220,11 @@ jobs:

bindings:
name: JS bindings
runs-on: self-hosted
runs-on: ${{ vars.CI_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-node@v4
Expand Down
6 changes: 4 additions & 2 deletions Cargo.lock

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

28 changes: 26 additions & 2 deletions crates/tapline-auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,29 @@ optional = true
default-features = false
features = ["sync-secret-service", "crypto-rust"]

[lints]
workspace = true
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61", features = [
"Win32_Foundation",
"Win32_Security",
"Win32_Security_Authorization",
"Win32_Storage_FileSystem",
"Win32_System_Threading",
] }

# Restated rather than inherited from the workspace, for one word: the workspace
# forbids unsafe outright, and `forbid` cannot be opted out of. Making the token
# file private on Windows means calling the Win32 security APIs by hand -- the
# crates that wrap them are either unmaintained or too new to trust with a
# refresh token -- so this crate says `deny` instead and `src/windows_acl.rs`
# allows it for itself. Nothing else here may, and on every other platform that
# module is not compiled at all. Everything below matches the workspace; if the
# workspace lints change, change them here too.
[lints.rust]
unsafe_code = "deny"

[lints.clippy]
unwrap_used = "deny"
expect_used = "deny"
panic = "deny"
indexing_slicing = "deny"
todo = "deny"
3 changes: 3 additions & 0 deletions crates/tapline-auth/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod local;
mod password;
mod sddl;
mod store;
#[cfg(windows)]
mod windows_acl;

pub use local::{
LocalAccount, discover, discover_in, libraries, most_recent, parse_libraries, parse_login_users,
Expand Down
Loading
Loading