Skip to content

Commit 03dec8b

Browse files
committed
Added org github tokens to check untracked private repositories
1 parent cdb439a commit 03dec8b

3 files changed

Lines changed: 116 additions & 9 deletions

File tree

.github/workflows/check-untracked-repos.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,22 @@ jobs:
3636
- name: Install Rust stable
3737
uses: ./.github/actions/setup-rust
3838

39+
# Used to detect private repos in allowed-github-orgs
40+
- name: Generate GitHub App tokens
41+
uses: ./.github/actions/generate-tokens
42+
id: generate-tokens
43+
with:
44+
app-id: ${{ secrets.SYNC_TEAM_GH_APP_ID }}
45+
private-key: ${{ secrets.SYNC_TEAM_GH_APP_PRIVATE_KEY }}
46+
3947
- name: Check untracked repositories
4048
id: check
49+
env:
50+
GITHUB_TOKEN_RUST_LANG: ${{ steps.generate-tokens.outputs.rust-lang-token }}
51+
GITHUB_TOKEN_RUST_LANG_DEPRECATED: ${{ steps.generate-tokens.outputs.rust-lang-deprecated-token }}
52+
GITHUB_TOKEN_RUST_LANG_NURSERY: ${{ steps.generate-tokens.outputs.rust-lang-nursery-token }}
53+
GITHUB_TOKEN_RUST_ANALYZER: ${{ steps.generate-tokens.outputs.rust-analyzer-token }}
54+
GITHUB_TOKEN_RUST_DEV_TOOLS: ${{ steps.generate-tokens.outputs.rust-dev-tools-token }}
4155
run: |
4256
cargo build --release
4357

src/api/github.rs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,28 @@ impl GitHubApi {
5858
require_auth: bool,
5959
method: Method,
6060
url: &str,
61+
) -> Result<RequestBuilder, Error> {
62+
self.prepare_with_token(require_auth, method, url, self.token.as_deref())
63+
}
64+
65+
fn prepare_with_token(
66+
&self,
67+
require_auth: bool,
68+
method: Method,
69+
url: &str,
70+
token: Option<&str>,
6171
) -> Result<RequestBuilder, Error> {
6272
let url = if url.starts_with("https://") {
6373
Cow::Borrowed(url)
6474
} else {
6575
Cow::Owned(format!("{API_BASE}{url}"))
6676
};
67-
if require_auth {
68-
self.require_auth()?;
77+
if require_auth && token.is_none() {
78+
bail!("missing environment variable {}", TOKEN_VAR);
6979
}
7080

7181
let mut req = self.http.request(method, url.as_ref());
72-
if let Some(token) = &self.token {
82+
if let Some(token) = token {
7383
req = req.header(
7484
header::AUTHORIZATION,
7585
HeaderValue::from_str(&format!("token {token}"))?,
@@ -122,11 +132,28 @@ impl GitHubApi {
122132
}
123133

124134
pub(crate) async fn get<T>(&self, url: &str) -> Result<T, Error>
135+
where
136+
T: serde::de::DeserializeOwned,
137+
{
138+
self.get_inner(url, self.token.as_deref()).await
139+
}
140+
141+
pub(crate) async fn get_with_token<T>(&self, url: &str, token: &str) -> Result<T, Error>
142+
where
143+
T: serde::de::DeserializeOwned,
144+
{
145+
self.get_inner(url, Some(token)).await
146+
}
147+
148+
async fn get_inner<T>(&self, url: &str, token: Option<&str>) -> Result<T, Error>
125149
where
126150
T: serde::de::DeserializeOwned,
127151
{
128152
loop {
129-
let response = self.prepare(false, Method::GET, url)?.send().await?;
153+
let response = self
154+
.prepare_with_token(false, Method::GET, url, token)?
155+
.send()
156+
.await?;
130157

131158
let status = response.status();
132159
if status != StatusCode::OK {
@@ -419,3 +446,34 @@ pub struct CommitInfo {
419446
pub repo_name: String,
420447
pub created_at: DateTime<Utc>,
421448
}
449+
450+
#[cfg(test)]
451+
mod tests {
452+
use super::GitHubApi;
453+
use reqwest::Method;
454+
use reqwest::header::AUTHORIZATION;
455+
456+
#[test]
457+
fn explicit_token_overrides_default_token() {
458+
let github = GitHubApi {
459+
http: reqwest::Client::new(),
460+
token: Some("default-token".into()),
461+
};
462+
463+
let request = github
464+
.prepare_with_token(
465+
false,
466+
Method::GET,
467+
"https://api.github.com/orgs/rust-lang/repos",
468+
Some("organization-token"),
469+
)
470+
.unwrap()
471+
.build()
472+
.unwrap();
473+
474+
assert_eq!(
475+
request.headers().get(AUTHORIZATION).unwrap(),
476+
"token organization-token"
477+
);
478+
}
479+
}

src/ci.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,15 +270,26 @@ async fn fetch_all_github_repos(
270270

271271
for org in orgs_to_monitor {
272272
debug!("Fetching repos for org: {}", org);
273+
274+
let token_env_var = github_token_env_var_name(org);
275+
let token = match std::env::var(&token_env_var) {
276+
Ok(token) => Some(token),
277+
Err(std::env::VarError::NotPresent) => None,
278+
Err(std::env::VarError::NotUnicode(_)) => {
279+
bail!("environment variable {token_env_var} is not valid Unicode")
280+
}
281+
};
282+
273283
let mut page = 1;
274284

275285
loop {
276286
let url = format!("orgs/{}/repos?per_page=100&page={}", org, page);
277287

278-
let repos: Vec<GitHubRepo> = github
279-
.get(&url)
280-
.await
281-
.with_context(|| format!("Failed to fetch repos for org: {}", org))?;
288+
let repos: Vec<GitHubRepo> = match token.as_deref() {
289+
Some(token) => github.get_with_token(&url, token).await,
290+
None => github.get(&url).await,
291+
}
292+
.with_context(|| format!("Failed to fetch repos for org: {}", org))?;
282293

283294
if repos.is_empty() {
284295
break;
@@ -295,6 +306,13 @@ async fn fetch_all_github_repos(
295306
Ok(all_repos)
296307
}
297308

309+
fn github_token_env_var_name(org: &str) -> String {
310+
format!(
311+
"GITHUB_TOKEN_{}",
312+
org.to_ascii_uppercase().replace('-', "_")
313+
)
314+
}
315+
298316
fn parse_tracked_repos(data: &Data) -> HashSet<(String, String)> {
299317
data.all_repos()
300318
.map(|repo| (repo.org.clone(), repo.name.clone()))
@@ -390,7 +408,10 @@ fn create_missing_repo_configs(data_dir: &Path, repos: &[UntrackedRepo]) -> anyh
390408

391409
#[cfg(test)]
392410
mod tests {
393-
use super::{GitHubRepo, UntrackedRepo, create_missing_repo_configs, find_untracked_repos};
411+
use super::{
412+
GitHubRepo, UntrackedRepo, create_missing_repo_configs, find_untracked_repos,
413+
github_token_env_var_name,
414+
};
394415
use crate::schema::Repo;
395416
use std::collections::HashSet;
396417

@@ -440,6 +461,20 @@ mod tests {
440461
);
441462
}
442463

464+
#[test]
465+
fn creates_github_token_environment_variable_name() {
466+
for (org, env_var) in [
467+
("rust-lang", "GITHUB_TOKEN_RUST_LANG"),
468+
("rust-lang-nursery", "GITHUB_TOKEN_RUST_LANG_NURSERY"),
469+
("rust-lang-deprecated", "GITHUB_TOKEN_RUST_LANG_DEPRECATED"),
470+
("rust-dev-tools", "GITHUB_TOKEN_RUST_DEV_TOOLS"),
471+
("rust-embedded", "GITHUB_TOKEN_RUST_EMBEDDED"),
472+
("rust-analyzer", "GITHUB_TOKEN_RUST_ANALYZER"),
473+
] {
474+
assert_eq!(github_token_env_var_name(org), env_var);
475+
}
476+
}
477+
443478
#[test]
444479
fn creates_parseable_repository_config() {
445480
let dir = tempfile::tempdir().unwrap();

0 commit comments

Comments
 (0)