mirror of
https://github.com/dsec-hub/dsec-discord-bot.git
synced 2026-09-22 07:44:26 +00:00
Finishes the AppState config hoisting (COR-12's groundwork was not present in the repo, so this ticket establishes it): AppState now carries guild_id, honeypot_channel_id, leetcode_channel_id, verified_role_id, logs_channel_id and weather_token, all parsed once in AppState::new(). A missing or unparseable id now produces one boot error naming every offending variable plus a pointer to .env.example, instead of a per-event .expect() panic (a data race under the multi-threaded runtime, since dotenv() calls the now-unsafe set_var). - main.rs: new fields, required_u64() collector, fail-fast with combined message. - events/message.rs: honeypot/create_leetcode_thread/on_message take &Data and read channel/guild ids from AppState; dropped dotenv() and env::var. - events/interaction_create.rs: deleted verified_role_id(); use data.state.verified_role_id. - commands/mods_only.rs: log_embed takes logs_channel_id: ChannelId; caller in message.rs passes data.state.logs_channel_id. - commands/weather.rs: WEATHER_TOKEN read once into AppState (non-fatal), passed to get_weather — required so the new CI grep guard can be clean. - .env.example: add LEETCODE_CHANNEL_ID. - .github/workflows/ci.yml: add "No environment reads outside main.rs" guard. grep -rn 'dotenv()\|env::var' src/ | grep -v '^src/main.rs:' now returns nothing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017XrE7F9ZuBWdQnS8CZvYDE
93 lines
3.7 KiB
YAML
93 lines
3.7 KiB
YAML
# Format, lint, build and test on every pull request.
|
|
#
|
|
# This must run on a GitHub-hosted runner and never on the club VPS. The runner
|
|
# deploy.yml uses lives on the club's own server and executes whatever a workflow
|
|
# tells it to, as root, so nothing triggered by a pull request may be pointed at
|
|
# it. Read that as a rule for this file, not a property of the repository: this
|
|
# repo is public, forking is on, and fork pull requests only need approval from a
|
|
# first-time contributor, so a fork that brings its own workflow can still reach
|
|
# that runner. See SEC-01.
|
|
#
|
|
# SEC-01: point the protect-main ruleset's required status check at this job's
|
|
# name ("fmt / clippy / build / test") so a red build blocks the merge that
|
|
# deploys.
|
|
|
|
name: CI
|
|
|
|
on:
|
|
pull_request:
|
|
|
|
# A newer push to the same pull request makes an in-flight run irrelevant.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
check:
|
|
name: fmt / clippy / build / test
|
|
runs-on: ubuntu-latest
|
|
# A cold build of this dependency tree (serenity, reqwest, image, exr) in
|
|
# release, then clippy, then test, is slow on a 4-vCPU hosted runner. This is
|
|
# a guard against a hung job, not a target.
|
|
timeout-minutes: 45
|
|
|
|
steps:
|
|
# Third-party and first-party actions alike are pinned to a full commit
|
|
# SHA: a tag is mutable and can be repointed at new code by whoever owns
|
|
# the action.
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
# Caches written by a pull_request run are scoped to that pull request, so
|
|
# this pays off across pushes to the same branch rather than across branches.
|
|
- name: Cache cargo registry and build artifacts
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
target
|
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-cargo-
|
|
|
|
# actions/runner-images documents rustfmt for ubuntu-24.04 but not clippy (it
|
|
# lists clippy only under ubuntu-22.04), while the image actually in use does
|
|
# ship it. Adding both components is a no-op when they are present and keeps
|
|
# this job working if that undocumented extra ever goes away. Print the
|
|
# versions too: clippy's lint set moves between releases, so knowing which one
|
|
# ran is what explains a lint that appeared from nowhere.
|
|
- name: Toolchain
|
|
run: |
|
|
rustup component add clippy rustfmt
|
|
cargo --version
|
|
cargo fmt --version
|
|
cargo clippy --version
|
|
|
|
- name: Format
|
|
run: cargo fmt --all -- --check
|
|
|
|
# Kept ahead of clippy and test: those resolve dependencies and would
|
|
# refresh Cargo.lock in place, so a stale lockfile would slip past
|
|
# --locked if they ran first.
|
|
- name: Build
|
|
run: cargo build --locked --release
|
|
|
|
- name: Clippy
|
|
run: cargo clippy --all-targets -- -D warnings
|
|
|
|
- name: Test
|
|
run: cargo test
|
|
|
|
# COL-BOT-05: configuration is parsed once in main.rs and stored on
|
|
# AppState. A per-event dotenv()/env::var read is a data race under the
|
|
# multi-threaded runtime (set_var is unsafe in edition 2024) and turns a
|
|
# config typo into a random future handler failure instead of a boot error.
|
|
- name: No environment reads outside main.rs
|
|
run: |
|
|
if grep -rn 'dotenv()\|env::var' src/ | grep -v '^src/main.rs:'; then
|
|
echo "::error::Environment variables must be read once in main.rs and stored on AppState."
|
|
exit 1
|
|
fi
|