Skip to content

Commit 616e261

Browse files
Copilotrumblefrog
andauthored
Support SourceMod builds from GitHub releases (1.13.7305+) (#31)
* Initial plan * Add GitHub releases support for SourceMod builds > 1.13.7304 Agent-Logs-Url: https://github.com/rumblefrog/setup-sp/sessions/99bf5575-87ba-4297-8d59-a0d422f1059c Co-authored-by: rumblefrog <6960234+rumblefrog@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rumblefrog <6960234+rumblefrog@users.noreply.github.com>
1 parent f8422e9 commit 616e261

7 files changed

Lines changed: 146 additions & 7 deletions

File tree

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,15 @@ jobs:
6666
echo Plugin version ${{ steps.setup_sp.outputs.plugin-version }}
6767
```
6868
69+
## Using a GitHub token to avoid rate limiting:
70+
71+
When fetching recent SourceMod builds (1.13.7305+), the action queries the GitHub releases API. To avoid rate limiting, pass a GitHub token:
72+
73+
```yaml
74+
- uses: rumblefrog/setup-sp@master
75+
with:
76+
version: '1.13.x'
77+
github-token: ${{ secrets.GITHUB_TOKEN }}
78+
```
79+
6980
A complete workflow example can be found [here](https://github.com/Sarrus1/DiscordWebhookAPI/blob/master/.github/workflows/master.yml).

action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ inputs:
1818
description: 'Whether spcomp should not be proxied to fix relative include path'
1919
required: false
2020
default: 'false'
21+
github-token:
22+
description: 'GitHub token for fetching SourceMod releases from GitHub API (to avoid rate limiting). Defaults to GITHUB_TOKEN environment variable.'
23+
required: false
24+
default: ''
2125
outputs:
2226
version:
2327
description: 'Version of the SP compiler used'

lib/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/index.js.LICENSE.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
2+
3+
/*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> */
4+
5+
/**
6+
* [js-md4]{@link https://github.com/emn178/js-md4}
7+
*
8+
* @namespace md4
9+
* @version 0.3.2
10+
* @author Yi-Cyuan Chen [emn178@gmail.com]
11+
* @copyright Yi-Cyuan Chen 2015-2027
12+
* @license MIT
13+
*/

src/structures/versioning.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,17 @@ export class Version {
4646
public toEndpoint(): string {
4747
return `${ENDPOINT}${this.major}.${this.minor}/sourcemod-${this.major}.${this.minor}.0-git${this.build}-${this.platform}.${this.archiveExt}`;
4848
}
49+
}
50+
51+
export class GithubVersion extends Version {
52+
private downloadUrl: string;
53+
54+
constructor(major: number, minor: number, build: number, platform: Platform, archiveExt: string, downloadUrl: string) {
55+
super(major, minor, build, platform, archiveExt);
56+
this.downloadUrl = downloadUrl;
57+
}
58+
59+
public toEndpoint(): string {
60+
return this.downloadUrl;
61+
}
4962
}

src/utils/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export const ENDPOINT: string = 'https://www.sourcemod.net/smdrop/';
22
export const MM_REGEX: RegExp = /href="(.*?)"/g;
33
export const BUILD_REGEX: RegExp = /href="sourcemod-[0-9]+.[0-9]+.[0-9]+-git([0-9]+)-(linux|windows|mac).(.*?)"/g;
4+
export const GITHUB_RELEASES_ENDPOINT: string = 'https://api.github.com/repos/alliedmodders/sourcemod/releases';
5+
export const GITHUB_ASSET_REGEX: RegExp = /^sourcemod-\d+\.\d+\.\d+-git(\d+)-(linux|windows|mac)\.(tar\.gz|zip)$/;

src/utils/scraper.ts

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,49 @@
1-
import { ENDPOINT, MM_REGEX, BUILD_REGEX } from './constants';
2-
import { Platform, Version, Versions, parsePlatform } from '../structures/versioning';
1+
import { ENDPOINT, MM_REGEX, BUILD_REGEX, GITHUB_RELEASES_ENDPOINT, GITHUB_ASSET_REGEX } from './constants';
2+
import { Platform, Version, GithubVersion, Versions, parsePlatform } from '../structures/versioning';
33
import { HttpClient } from 'typed-rest-client/HttpClient';
4+
import { BearerCredentialHandler } from 'typed-rest-client/Handlers';
45
import to from 'await-to-js';
6+
import { warning, getInput } from '@actions/core';
57

68
const client = new HttpClient('setup-sp');
79

10+
function getGithubClient(): HttpClient {
11+
const token = getInput('github-token') || process.env.GITHUB_TOKEN;
12+
if (token) {
13+
return new HttpClient('setup-sp', [new BearerCredentialHandler(token)]);
14+
}
15+
return new HttpClient('setup-sp');
16+
}
17+
818
export async function getVersions(): Promise<Versions> {
19+
const results = await Promise.allSettled([
20+
getSmDropVersions(),
21+
getGithubVersions(),
22+
]);
23+
24+
let versions: Versions = {};
25+
26+
if (results[0].status === 'fulfilled') {
27+
Object.assign(versions, results[0].value);
28+
} else {
29+
warning(`Failed to fetch versions from smdrop: ${results[0].reason}`);
30+
}
31+
32+
if (results[1].status === 'fulfilled') {
33+
// GitHub releases take precedence over smdrop for overlapping builds
34+
Object.assign(versions, results[1].value);
35+
} else {
36+
warning(`Failed to fetch versions from GitHub releases: ${results[1].reason}`);
37+
}
38+
39+
if (Object.keys(versions).length === 0) {
40+
throw new Error('Failed to fetch SourceMod versions from all sources');
41+
}
42+
43+
return versions;
44+
}
45+
46+
async function getSmDropVersions(): Promise<Versions> {
947
const [ err, res ] = await to(client.get(ENDPOINT));
1048

1149
if (err || !res || res.message.statusCode !== 200) {
@@ -14,10 +52,6 @@ export async function getVersions(): Promise<Versions> {
1452

1553
let versions: Versions = {};
1654

17-
if (err) {
18-
return versions;
19-
}
20-
2155
const body = await res.readBody();
2256

2357
let match, promises: Promise<void>[] = [];
@@ -44,6 +78,68 @@ export async function getVersions(): Promise<Versions> {
4478
return versions;
4579
}
4680

81+
async function getGithubVersions(): Promise<Versions> {
82+
const githubClient = getGithubClient();
83+
let versions: Versions = {};
84+
let page = 1;
85+
const perPage = 100;
86+
87+
while (true) {
88+
const url = `${GITHUB_RELEASES_ENDPOINT}?per_page=${perPage}&page=${page}`;
89+
const [err, res] = await to(githubClient.get(url));
90+
91+
if (err || !res || res.message.statusCode !== 200) {
92+
if (page === 1) {
93+
throw new Error(`Failed to fetch releases from GitHub: ${err?.message ?? res?.message.statusCode}`);
94+
}
95+
break;
96+
}
97+
98+
const body = await res.readBody();
99+
const releases: any[] = JSON.parse(body);
100+
101+
if (!releases || releases.length === 0) {
102+
break;
103+
}
104+
105+
for (const release of releases) {
106+
if (!release.tag_name) continue;
107+
108+
// Tag format: "1.13.0.7316"
109+
const tagParts = release.tag_name.split('.');
110+
if (tagParts.length !== 4) continue;
111+
112+
const major = parseInt(tagParts[0]);
113+
const minor = parseInt(tagParts[1]);
114+
const build = parseInt(tagParts[3]);
115+
116+
if (isNaN(major) || isNaN(minor) || isNaN(build)) continue;
117+
118+
for (const asset of release.assets as any[]) {
119+
const match = GITHUB_ASSET_REGEX.exec(asset.name);
120+
if (!match) continue;
121+
122+
const platform = parsePlatform(match[2]);
123+
124+
if (process.platform === 'win32' && platform !== Platform.Windows) continue;
125+
if (process.platform === 'darwin' && platform !== Platform.Mac) continue;
126+
if (process.platform === 'linux' && platform !== Platform.Linux) continue;
127+
128+
const v = new GithubVersion(major, minor, build, platform, match[3], asset.browser_download_url);
129+
versions[v.toString()] = v;
130+
}
131+
}
132+
133+
if (releases.length < perPage) {
134+
break;
135+
}
136+
137+
page++;
138+
}
139+
140+
return versions;
141+
}
142+
47143
async function getBuilds(endpoint: string, versions: Versions, major: number, minor: number) {
48144
const [ err, res ] = await to(client.get(endpoint));
49145

0 commit comments

Comments
 (0)