Compare commits

...

6 Commits

Author SHA1 Message Date
Josh Soref
d0d0f0c194 Merge 98cae64d4a into 3041bf56c9 2025-01-02 18:02:49 -05:00
Josh Soref
98cae64d4a spelling: spell checker
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-01-02 16:07:55 -05:00
Josh Soref
4b7aaecd5c spelling: occurred
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-01-02 15:39:01 -05:00
Josh Soref
2c2977fbc8 spelling: macos
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-01-02 15:39:01 -05:00
Josh Soref
d4c9e7cb2c spelling: httpm
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-01-02 15:39:01 -05:00
Tobias
3041bf56c9 feat: fallback to "raw" endpoint for manifest when rate limit is reached (#496)
* feat: fallback to "raw" endpoint for manifest when rate limit is reached

* add information about raw access to the README

* prettier

* update cross-spawn to 7.0.6 to fix vulnerability
2024-11-25 12:37:21 -06:00
7 changed files with 110 additions and 23 deletions

View File

@@ -242,18 +242,14 @@ documentation.
## Using `setup-go` on GHES
`setup-go` comes pre-installed on the appliance with GHES if Actions is enabled. When dynamically downloading Go
distributions, `setup-go` downloads distributions from [`actions/go-versions`](https://github.com/actions/go-versions)
on github.com (outside of the appliance). These calls to `actions/go-versions` are made via unauthenticated requests,
which are limited
to [60 requests per hour per IP](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting). If
more requests are made within the time frame, then you will start to see rate-limit errors during downloading that looks
like: `##[error]API rate limit exceeded for...`. After that error the action will try to download versions directly
from https://storage.googleapis.com/golang, but it also can have rate limit so it's better to put token.
`setup-go` comes pre-installed on the appliance with GHES if Actions is enabled.
When dynamically downloading Go distributions, `setup-go` downloads distributions from [`actions/go-versions`](https://github.com/actions/go-versions) on github.com (outside of the appliance).
To get a higher rate limit, you
can [generate a personal access token on github.com](https://github.com/settings/tokens/new) and pass it as the `token`
input for the action:
These calls to `actions/go-versions` are made via unauthenticated requests, which are limited to [60 requests per hour per IP](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting).
If more requests are made within the time frame, then the action leverages the `raw API` to retrieve the version-manifest. This approach does not impose a rate limit and hence facilitates unrestricted consumption. This is particularly beneficial for GHES runners, which often share the same IP, to avoid the quick exhaustion of the unauthenticated rate limit.
If that fails as well the action will try to download versions directly from https://storage.googleapis.com/golang.
If that fails as well you can get a higher rate limit with [generating a personal access token on github.com](https://github.com/settings/tokens/new) and passing it as the `token` input to the action:
```yaml
uses: actions/setup-go@v5
@@ -262,8 +258,7 @@ with:
go-version: '1.18'
```
If the runner is not able to access github.com, any Go versions requested during a workflow run must come from the
runner's tool cache.
If the runner is not able to access github.com, any Go versions requested during a workflow run must come from the runner's tool cache.
See "[Setting up the tool cache on self-hosted runners without internet access](https://docs.github.com/en/enterprise-server@3.2/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)"
for more information.

View File

@@ -51,7 +51,7 @@ describe('restoreCache', () => {
);
});
it('should inform if cache hit is not occured', async () => {
it('should inform if cache hit is not occurred', async () => {
//Arrange
hashFilesSpy.mockImplementation((somePath: string) => {
return new Promise<string>(resolve => {
@@ -74,7 +74,7 @@ describe('restoreCache', () => {
expect(infoSpy).toHaveBeenCalledWith(`Cache is not found`);
});
it('should set output if cache hit is occured', async () => {
it('should set output if cache hit is occurred', async () => {
//Arrange
hashFilesSpy.mockImplementation((somePath: string) => {
return new Promise<string>(resolve => {

View File

@@ -7,6 +7,7 @@ import osm, {type} from 'os';
import path from 'path';
import * as main from '../src/main';
import * as im from '../src/installer';
import * as httpm from '@actions/http-client';
import goJsonData from './data/golang-dl.json';
import matchers from '../matchers.json';
@@ -46,6 +47,7 @@ describe('setup-go', () => {
let execSpy: jest.SpyInstance;
let getManifestSpy: jest.SpyInstance;
let getAllVersionsSpy: jest.SpyInstance;
let httpmGetJsonSpy: jest.SpyInstance;
beforeAll(async () => {
process.env['GITHUB_ENV'] = ''; // Stub out Environment file functionality so we can verify it writes to standard out (toolkit is backwards compatible)
@@ -90,6 +92,9 @@ describe('setup-go', () => {
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
getAllVersionsSpy = jest.spyOn(im, 'getManifest');
// httpm
httpmGetJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
// io
whichSpy = jest.spyOn(io, 'which');
existsSpy = jest.spyOn(fs, 'existsSync');
@@ -138,7 +143,7 @@ describe('setup-go', () => {
expect(main.parseGoVersion(goVersionOutput)).toBe('1.16.6');
});
it('can find 1.9.7 from manifest on osx', async () => {
it('can find 1.9.7 from manifest on macOS', async () => {
os.platform = 'darwin';
os.arch = 'x64';
@@ -151,6 +156,21 @@ describe('setup-go', () => {
);
});
it('should return manifest from repo', async () => {
const manifest = await im.getManifest(undefined);
expect(manifest).toEqual(goTestManifest);
});
it('should return manifest from raw URL if repo fetch fails', async () => {
getManifestSpy.mockRejectedValue(new Error('Fetch failed'));
httpmGetJsonSpy.mockResolvedValue({
result: goTestManifest
});
const manifest = await im.getManifest(undefined);
expect(httpmGetJsonSpy).toHaveBeenCalled();
expect(manifest).toEqual(goTestManifest);
});
it('can find 1.9 from manifest on linux', async () => {
os.platform = 'linux';
os.arch = 'x64';
@@ -790,6 +810,9 @@ describe('setup-go', () => {
getManifestSpy.mockImplementation(() => {
throw new Error('Unable to download manifest');
});
httpmGetJsonSpy.mockRejectedValue(
new Error('Unable to download manifest from raw URL')
);
getAllVersionsSpy.mockImplementationOnce(() => undefined);
dlSpy.mockImplementation(async () => '/some/temp/path');

30
dist/setup/index.js vendored
View File

@@ -88259,6 +88259,10 @@ const sys = __importStar(__nccwpck_require__(5632));
const fs_1 = __importDefault(__nccwpck_require__(7147));
const os_1 = __importDefault(__nccwpck_require__(2037));
const utils_1 = __nccwpck_require__(1314);
const MANIFEST_REPO_OWNER = 'actions';
const MANIFEST_REPO_NAME = 'go-versions';
const MANIFEST_REPO_BRANCH = 'main';
const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`;
function getGo(versionSpec_1, checkLatest_1, auth_1) {
return __awaiter(this, arguments, void 0, function* (versionSpec, checkLatest, auth, arch = os_1.default.arch()) {
var _a;
@@ -88433,10 +88437,34 @@ function extractGoArchive(archivePath) {
exports.extractGoArchive = extractGoArchive;
function getManifest(auth) {
return __awaiter(this, void 0, void 0, function* () {
return tc.getManifestFromRepo('actions', 'go-versions', auth, 'main');
try {
return yield getManifestFromRepo(auth);
}
catch (err) {
core.debug('Fetching the manifest via the API failed.');
if (err instanceof Error) {
core.debug(err.message);
}
}
return yield getManifestFromURL();
});
}
exports.getManifest = getManifest;
function getManifestFromRepo(auth) {
core.debug(`Getting manifest from ${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}@${MANIFEST_REPO_BRANCH}`);
return tc.getManifestFromRepo(MANIFEST_REPO_OWNER, MANIFEST_REPO_NAME, auth, MANIFEST_REPO_BRANCH);
}
function getManifestFromURL() {
return __awaiter(this, void 0, void 0, function* () {
core.debug('Falling back to fetching the manifest using raw URL.');
const http = new httpm.HttpClient('tool-cache');
const response = yield http.getJson(MANIFEST_URL);
if (!response.result) {
throw new Error(`Unable to get manifest from ${MANIFEST_URL}`);
}
return response.result;
});
}
function getInfoFromManifest(versionSpec_1, stable_1, auth_1) {
return __awaiter(this, arguments, void 0, function* (versionSpec, stable, auth, arch = os_1.default.arch(), manifest) {
let info = null;

View File

@@ -27,7 +27,7 @@ Pull requests are the easiest way to contribute changes to git repos at GitHub.
- Please check that no one else has already created a pull request with these changes
- Use a "feature branch" for your changes. That separates the changes in the pull request from your other changes and makes it easy to edit/amend commits in the pull request
- Make sure your changes are formatted correctly and consistently with the rest of the documentation
- Re-read what you wrote, and run a spellchecker on it to make sure you didn't miss anything
- Re-read what you wrote, and run a spell checker on it to make sure you didn't miss anything
- If your pull request is connected to an open issue, please, leave a link to this issue in the `Related issue:` section
- If you later need to add new commits to the pull request, you can simply commit the changes to the local branch and then push them. The pull request gets automatically updated

7
package-lock.json generated
View File

@@ -2504,10 +2504,11 @@
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",

View File

@@ -8,6 +8,11 @@ import fs from 'fs';
import os from 'os';
import {StableReleaseAlias} from './utils';
const MANIFEST_REPO_OWNER = 'actions';
const MANIFEST_REPO_NAME = 'go-versions';
const MANIFEST_REPO_BRANCH = 'main';
const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`;
type InstallationType = 'dist' | 'manifest';
export interface IGoVersionFile {
@@ -274,8 +279,43 @@ export async function extractGoArchive(archivePath: string): Promise<string> {
return extPath;
}
export async function getManifest(auth: string | undefined) {
return tc.getManifestFromRepo('actions', 'go-versions', auth, 'main');
export async function getManifest(
auth: string | undefined
): Promise<tc.IToolRelease[]> {
try {
return await getManifestFromRepo(auth);
} catch (err) {
core.debug('Fetching the manifest via the API failed.');
if (err instanceof Error) {
core.debug(err.message);
}
}
return await getManifestFromURL();
}
function getManifestFromRepo(
auth: string | undefined
): Promise<tc.IToolRelease[]> {
core.debug(
`Getting manifest from ${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}@${MANIFEST_REPO_BRANCH}`
);
return tc.getManifestFromRepo(
MANIFEST_REPO_OWNER,
MANIFEST_REPO_NAME,
auth,
MANIFEST_REPO_BRANCH
);
}
async function getManifestFromURL(): Promise<tc.IToolRelease[]> {
core.debug('Falling back to fetching the manifest using raw URL.');
const http: httpm.HttpClient = new httpm.HttpClient('tool-cache');
const response = await http.getJson<tc.IToolRelease[]>(MANIFEST_URL);
if (!response.result) {
throw new Error(`Unable to get manifest from ${MANIFEST_URL}`);
}
return response.result;
}
export async function getInfoFromManifest(