add commentary and (unused) check for token applicability
This commit is contained in:
parent
3b195a9eb6
commit
f4d66203b9
2 changed files with 90 additions and 4 deletions
25
README.md
25
README.md
|
@ -24,10 +24,10 @@ zig build
|
||||||
2. Run the application:
|
2. Run the application:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./zig-out/bin/release-tracker config.json
|
./zig-out/bin/release-tracker config.json [output filename]
|
||||||
```
|
```
|
||||||
|
|
||||||
3. The RSS feed will be generated as `releases.xml`
|
3. The RSS feed will be generated as `releases.xml` by default
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ Create a `config.json` file with your API tokens:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"github_token": "your_github_token",
|
"github_token": "your_github_token",
|
||||||
"gitlab_token": "your_gitlab_token",
|
"gitlab_token": "your_gitlab_token",
|
||||||
"codeberg_token": "your_codeberg_token",
|
"codeberg_token": "your_codeberg_token",
|
||||||
"sourcehut": {
|
"sourcehut": {
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
@ -50,11 +50,28 @@ Create a `config.json` file with your API tokens:
|
||||||
|
|
||||||
### API Token Setup
|
### API Token Setup
|
||||||
|
|
||||||
- **GitHub**: Create a Personal Access Token with `public_repo` and `user` scopes
|
- **GitHub**: Create a Personal Access Token with and `user:read` scope. Classic is preferred (see note)
|
||||||
- **GitLab**: Create a Personal Access Token with `read_api` scope
|
- **GitLab**: Create a Personal Access Token with `read_api` scope
|
||||||
- **Codeberg**: Create an Access Token in your account settings
|
- **Codeberg**: Create an Access Token in your account settings
|
||||||
- **SourceHut**: No token required for public repositories. Specify repositories to track in the configuration.
|
- **SourceHut**: No token required for public repositories. Specify repositories to track in the configuration.
|
||||||
|
|
||||||
|
Note on GitHub PATs. Some GitHub orgs will place additional restrictions on
|
||||||
|
PATs. If your token does not align with those policies, the GitHub stars API
|
||||||
|
will **silently discard** repos from that org. The only way to tell something
|
||||||
|
is off is by detecting the starred repositories count at
|
||||||
|
https://github.com/<username>?tab=stars is more than what is reported in the
|
||||||
|
application output.
|
||||||
|
|
||||||
|
There is a `checkForInaccessibleRepos` function in the GitHub provider that
|
||||||
|
can detect this, but is limited to the `aws` organization, which may or may
|
||||||
|
not apply in all situations. For this reason, it is included in the code, but
|
||||||
|
disabled. If needed, uncomment the call and choose a repo from your enterprise
|
||||||
|
of choice.
|
||||||
|
|
||||||
|
These org policies do not seem to effect classic tokens, so the best approach
|
||||||
|
with GitHub is to create and use a classic token instead of the new fine-grained
|
||||||
|
tokens.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Run the test suite:
|
Run the test suite:
|
||||||
|
|
|
@ -54,6 +54,10 @@ pub fn fetchReleases(self: *Self, allocator: Allocator) !ArrayList(Release) {
|
||||||
|
|
||||||
const starred_duration: u64 = @intCast(starred_end_time - starred_start_time);
|
const starred_duration: u64 = @intCast(starred_end_time - starred_start_time);
|
||||||
std.log.debug("GitHub: Found {} starred repositories in {}ms", .{ starred_repos.items.len, starred_duration });
|
std.log.debug("GitHub: Found {} starred repositories in {}ms", .{ starred_repos.items.len, starred_duration });
|
||||||
|
|
||||||
|
// Check for potentially inaccessible repositories due to enterprise policies
|
||||||
|
// try checkForInaccessibleRepos(allocator, &client, self.token, starred_repos.items);
|
||||||
|
|
||||||
std.log.debug("GitHub: Processing {} starred repositories with thread pool...", .{starred_repos.items.len});
|
std.log.debug("GitHub: Processing {} starred repositories with thread pool...", .{starred_repos.items.len});
|
||||||
|
|
||||||
const thread_start_time = std.time.milliTimestamp();
|
const thread_start_time = std.time.milliTimestamp();
|
||||||
|
@ -423,6 +427,71 @@ fn compareReleasesByDate(context: void, a: Release, b: Release) bool {
|
||||||
return a.published_at > b.published_at;
|
return a.published_at > b.published_at;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn checkForInaccessibleRepos(allocator: Allocator, client: *http.Client, token: []const u8, starred_repos: [][]const u8) !void {
|
||||||
|
const is_test = @import("builtin").is_test;
|
||||||
|
if (is_test) return; // Skip in tests
|
||||||
|
|
||||||
|
// List of repositories that are commonly affected by enterprise policies
|
||||||
|
const problematic_repos = [_][]const u8{
|
||||||
|
"aws/language-server-runtimes",
|
||||||
|
"aws/aws-cli",
|
||||||
|
"aws/aws-sdk-js",
|
||||||
|
"aws/aws-cdk",
|
||||||
|
};
|
||||||
|
|
||||||
|
const auth_header = try std.fmt.allocPrint(allocator, "Bearer {s}", .{token});
|
||||||
|
defer allocator.free(auth_header);
|
||||||
|
|
||||||
|
for (problematic_repos) |repo| {
|
||||||
|
// Check if this repo is in our starred list
|
||||||
|
var found_in_starred = false;
|
||||||
|
for (starred_repos) |starred_repo| {
|
||||||
|
if (std.mem.eql(u8, starred_repo, repo)) {
|
||||||
|
found_in_starred = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_in_starred) {
|
||||||
|
// Check if we can access this repository directly to see if it's a policy issue
|
||||||
|
const check_url = try std.fmt.allocPrint(allocator, "https://api.github.com/user/starred/{s}", .{repo});
|
||||||
|
defer allocator.free(check_url);
|
||||||
|
|
||||||
|
const uri = std.Uri.parse(check_url) catch continue;
|
||||||
|
|
||||||
|
var server_header_buffer: [16 * 1024]u8 = undefined;
|
||||||
|
var req = client.open(.GET, uri, .{
|
||||||
|
.server_header_buffer = &server_header_buffer,
|
||||||
|
.extra_headers = &.{
|
||||||
|
.{ .name = "Authorization", .value = auth_header },
|
||||||
|
.{ .name = "Accept", .value = "application/vnd.github.v3+json" },
|
||||||
|
.{ .name = "User-Agent", .value = "release-tracker/1.0" },
|
||||||
|
},
|
||||||
|
}) catch continue;
|
||||||
|
defer req.deinit();
|
||||||
|
|
||||||
|
req.send() catch continue;
|
||||||
|
req.wait() catch continue;
|
||||||
|
|
||||||
|
if (req.response.status == .forbidden) {
|
||||||
|
// Try to read the error response for more details
|
||||||
|
const error_body = req.reader().readAllAlloc(allocator, 4096) catch "";
|
||||||
|
defer if (error_body.len > 0) allocator.free(error_body);
|
||||||
|
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
if (std.mem.indexOf(u8, error_body, "enterprise") != null or
|
||||||
|
std.mem.indexOf(u8, error_body, "personal access token") != null or
|
||||||
|
std.mem.indexOf(u8, error_body, "fine-grained") != null)
|
||||||
|
{
|
||||||
|
stderr.print("GitHub: Repository '{s}' may be starred but is inaccessible due to enterprise policies: {s}\n", .{ repo, error_body }) catch {};
|
||||||
|
} else {
|
||||||
|
stderr.print("GitHub: Repository '{s}' is not accessible (HTTP 403): {s}\n", .{ repo, error_body }) catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "github provider" {
|
test "github provider" {
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue