Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,15 @@ jobs:
echo Plugin version ${{ steps.setup_sp.outputs.plugin-version }}
```

## Using a GitHub token to avoid rate limiting:

When fetching recent SourceMod builds (1.13.7305+), the action queries the GitHub releases API. To avoid rate limiting, pass a GitHub token:

```yaml
- uses: rumblefrog/setup-sp@master
with:
version: '1.13.x'
github-token: ${{ secrets.GITHUB_TOKEN }}
```

A complete workflow example can be found [here](https://github.com/Sarrus1/DiscordWebhookAPI/blob/master/.github/workflows/master.yml).
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ inputs:
description: 'Whether spcomp should not be proxied to fix relative include path'
required: false
default: 'false'
github-token:
description: 'GitHub token for fetching SourceMod releases from GitHub API (to avoid rate limiting). Defaults to GITHUB_TOKEN environment variable.'
required: false
default: ''
outputs:
version:
description: 'Version of the SP compiler used'
Expand Down
2 changes: 1 addition & 1 deletion lib/index.js

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions lib/index.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */

/*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> */

/**
* [js-md4]{@link https://github.com/emn178/js-md4}
*
* @namespace md4
* @version 0.3.2
* @author Yi-Cyuan Chen [emn178@gmail.com]
* @copyright Yi-Cyuan Chen 2015-2027
* @license MIT
*/
13 changes: 13 additions & 0 deletions src/structures/versioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,17 @@ export class Version {
public toEndpoint(): string {
return `${ENDPOINT}${this.major}.${this.minor}/sourcemod-${this.major}.${this.minor}.0-git${this.build}-${this.platform}.${this.archiveExt}`;
}
}

export class GithubVersion extends Version {
private downloadUrl: string;

constructor(major: number, minor: number, build: number, platform: Platform, archiveExt: string, downloadUrl: string) {
super(major, minor, build, platform, archiveExt);
this.downloadUrl = downloadUrl;
}

public toEndpoint(): string {
return this.downloadUrl;
}
}
2 changes: 2 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const ENDPOINT: string = 'https://www.sourcemod.net/smdrop/';
export const MM_REGEX: RegExp = /href="(.*?)"/g;
export const BUILD_REGEX: RegExp = /href="sourcemod-[0-9]+.[0-9]+.[0-9]+-git([0-9]+)-(linux|windows|mac).(.*?)"/g;
export const GITHUB_RELEASES_ENDPOINT: string = 'https://api.github.com/repos/alliedmodders/sourcemod/releases';
export const GITHUB_ASSET_REGEX: RegExp = /^sourcemod-\d+\.\d+\.\d+-git(\d+)-(linux|windows|mac)\.(tar\.gz|zip)$/;
108 changes: 102 additions & 6 deletions src/utils/scraper.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,49 @@
import { ENDPOINT, MM_REGEX, BUILD_REGEX } from './constants';
import { Platform, Version, Versions, parsePlatform } from '../structures/versioning';
import { ENDPOINT, MM_REGEX, BUILD_REGEX, GITHUB_RELEASES_ENDPOINT, GITHUB_ASSET_REGEX } from './constants';
import { Platform, Version, GithubVersion, Versions, parsePlatform } from '../structures/versioning';
import { HttpClient } from 'typed-rest-client/HttpClient';
import { BearerCredentialHandler } from 'typed-rest-client/Handlers';
import to from 'await-to-js';
import { warning, getInput } from '@actions/core';

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

function getGithubClient(): HttpClient {
const token = getInput('github-token') || process.env.GITHUB_TOKEN;
if (token) {
return new HttpClient('setup-sp', [new BearerCredentialHandler(token)]);
}
return new HttpClient('setup-sp');
}

export async function getVersions(): Promise<Versions> {
const results = await Promise.allSettled([
getSmDropVersions(),
getGithubVersions(),
]);

let versions: Versions = {};

if (results[0].status === 'fulfilled') {
Object.assign(versions, results[0].value);
} else {
warning(`Failed to fetch versions from smdrop: ${results[0].reason}`);
}

if (results[1].status === 'fulfilled') {
// GitHub releases take precedence over smdrop for overlapping builds
Object.assign(versions, results[1].value);
} else {
warning(`Failed to fetch versions from GitHub releases: ${results[1].reason}`);
}

if (Object.keys(versions).length === 0) {
throw new Error('Failed to fetch SourceMod versions from all sources');
}

return versions;
}

async function getSmDropVersions(): Promise<Versions> {
const [ err, res ] = await to(client.get(ENDPOINT));

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

let versions: Versions = {};

if (err) {
return versions;
}

const body = await res.readBody();

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

async function getGithubVersions(): Promise<Versions> {
const githubClient = getGithubClient();
let versions: Versions = {};
let page = 1;
const perPage = 100;

while (true) {
const url = `${GITHUB_RELEASES_ENDPOINT}?per_page=${perPage}&page=${page}`;
const [err, res] = await to(githubClient.get(url));

if (err || !res || res.message.statusCode !== 200) {
if (page === 1) {
throw new Error(`Failed to fetch releases from GitHub: ${err?.message ?? res?.message.statusCode}`);
}
break;
}

const body = await res.readBody();
const releases: any[] = JSON.parse(body);

if (!releases || releases.length === 0) {
break;
}

for (const release of releases) {
if (!release.tag_name) continue;

// Tag format: "1.13.0.7316"
const tagParts = release.tag_name.split('.');
if (tagParts.length !== 4) continue;

const major = parseInt(tagParts[0]);
const minor = parseInt(tagParts[1]);
const build = parseInt(tagParts[3]);

if (isNaN(major) || isNaN(minor) || isNaN(build)) continue;

for (const asset of release.assets as any[]) {
const match = GITHUB_ASSET_REGEX.exec(asset.name);
if (!match) continue;

const platform = parsePlatform(match[2]);

if (process.platform === 'win32' && platform !== Platform.Windows) continue;
if (process.platform === 'darwin' && platform !== Platform.Mac) continue;
if (process.platform === 'linux' && platform !== Platform.Linux) continue;

const v = new GithubVersion(major, minor, build, platform, match[3], asset.browser_download_url);
versions[v.toString()] = v;
}
}

if (releases.length < perPage) {
break;
}

page++;
}

return versions;
}

async function getBuilds(endpoint: string, versions: Versions, major: number, minor: number) {
const [ err, res ] = await to(client.get(endpoint));

Expand Down
Loading