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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
### **WORK IN PROGRESS**

* (@mcm1957) Special handling of some core adapters added.
* (@mcm1957) Fixed: githubToken and axios instance are now isolated per check; concurrent checks no longer share authorization state or redaction context (see [#1193](https://github.com/ioBroker/ioBroker.repochecker/issues/1193)).

### 5.22.16 (2026-10-13)

Expand Down
6 changes: 3 additions & 3 deletions lib/M1000_IOPackageJson.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ async function validateIOPackageSchema(context) {
const schemaUrl = config.schemaUrls['io-package'];
common.debug(`Downloading schema from ${schemaUrl}`);

const schemaData = await common.downloadURL(schemaUrl, false);
const schemaData = await common.downloadURL(context, schemaUrl, false);
if (!schemaData) {
context.warnings.push(`[W1107] Could not download ${schemaUrl} for validation of io-package.json`);
return context;
Expand Down Expand Up @@ -876,7 +876,7 @@ async function checkIOPackageJson(context) {
);
}

if (!((await common.downloadURL(licenseInformationLink, false)) || '').trim()) {
if (!((await common.downloadURL(context, licenseInformationLink, false)) || '').trim()) {
context.warnings.push('[W1138] "common.licenseInformation.link" should point to a readable URL');
} else {
context.checks.push('"common.licenseInformation.link" points to a readable URL');
Expand Down Expand Up @@ -1682,7 +1682,7 @@ async function checkIOPackageJson(context) {
const url = context.ioPackageJson.common.extIcon.replace(context.githubUrl, '');
icon = await common.getFile(null, url, true); // TO BE ADAPTED
} else {
icon = await common.downloadURL(context.ioPackageJson.common.extIcon, true);
icon = await common.downloadURL(context, context.ioPackageJson.common.extIcon, true);
}
context.checks.push('"extIcon" could be downloaded');
//console.log(icon);
Expand Down
2 changes: 1 addition & 1 deletion lib/M5500__JsonConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ async function validateJsonConfigSchema(context, filePath, jsonConfigData) {
const schemaUrl = config.schemaUrls.jsonConfig;
common.debug(`Downloading schema from ${schemaUrl}`);

const schemaData = await common.downloadURL(schemaUrl, false);
const schemaData = await common.downloadURL(context, schemaUrl, false);
if (!schemaData) {
context.warnings.push(`[W5513] Could not download ${schemaUrl} for validation of ${filePath}`);
return;
Expand Down
44 changes: 27 additions & 17 deletions lib/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@
const githubToken = process.env.OWN_GITHUB_TOKEN;
const redactedValue = '***REDACTED***';

// Private axios instance used within this module. Created by initAxios().
// We deliberately avoid a global axios instance so that request specific
// configuration (e.g. an individual github token) does not leak between requests.
// Fallback axios instance – set by the most recent initAxios() call.
// Prefer context.axios (set per check) over this module-level fallback so that
// concurrent checks do not share the same authorized instance.
let axiosInstance = null;

// The github token actually used for authorization. Kept for redaction of log output.
let effectiveGithubToken = githubToken;
// All github tokens ever registered across all initAxios() calls, used so that
// redactString() redacts every token regardless of which check is currently active.
const knownTokens = new Set();
if (githubToken) {
knownTokens.add(githubToken);
}

let gDebug = false;
let gInfo = true;
Expand Down Expand Up @@ -79,7 +83,10 @@
// determine which github token to use
const contextToken = context && typeof context.githubToken === 'string' ? context.githubToken.trim() : '';
const token = contextToken || githubToken;
effectiveGithubToken = token;
// Register the token so redactString() can redact it for all concurrent checks.
if (token) {
knownTokens.add(token);
}

if (token) {
info(
Expand Down Expand Up @@ -162,11 +169,10 @@

function redactString(value) {
let result = value;
if (githubToken && result.includes(githubToken)) {
result = result.replaceAll(githubToken, redactedValue);
}
if (effectiveGithubToken && effectiveGithubToken !== githubToken && result.includes(effectiveGithubToken)) {
result = result.replaceAll(effectiveGithubToken, redactedValue);
for (const token of knownTokens) {
if (result.includes(token)) {
result = result.replaceAll(token, redactedValue);
}
}
return result;
}
Expand Down Expand Up @@ -301,14 +307,16 @@
return languages.filter(lang => !langObj[lang]);
}

async function downloadURL(url, binary) {
async function downloadURL(context, url, binary) {
debug(`Download file ${url} ${binary ? ' using binary mode' : ''})`);
const options = {};
options.responseType = binary ? 'arraybuffer' : 'text';
// Use the per-check axios instance from context when available so that concurrent
// checks do not share the same authorized instance (see issue #1193).
const instance = (context && context.axios) || axiosInstance;
try {
//const response = await axios(githubUrl + (path || ''), options);
// @ts-expect-error options work this way
const response = await axiosInstance(url, options);
const response = await instance(url, options);
debug(`Download succeded`);
return response.data;
} catch (e) {
Expand All @@ -333,7 +341,7 @@
const url = context
? `https://cdn.jsdelivr.net/gh/${context.repository}@${context.lastCommitSha}${path || ''}`
: path;
file = await downloadURL(url, binary);
file = await downloadURL(context, url, binary);
} else {
debug(`Download local ${path || ''}`);
// remove first / from path
Expand Down Expand Up @@ -361,8 +369,10 @@
const url = `https://data.jsdelivr.com/v1/packages/gh/${context.repository}@${context.lastCommitSha}?structure=flat`;
debug(`Download ${url}`);
const filesList = [];
// Use the per-check axios instance so concurrent checks stay isolated (see issue #1193).
const instance = context.axios || axiosInstance;
try {
const response = await axiosInstance(url);
const response = await instance(url);
debug(`download succeded`);
if (response.data?.files) {
response.data.files.forEach(fileInfo => filesList.push(fileInfo.name));
Expand All @@ -375,7 +385,7 @@
const url2 = `https://api.github.com/repos/${context.repository}/git/trees/${context.lastCommitSha}?recursive=1`;
debug(`Download ${url} [RETRY]`);
try {
const response = await axiosInstance(url2);
const response = await instance(url2);
debug(`download succeded [RETRY]`);
if (response.data?.tree) {
response.data.tree.forEach(fileInfo => filesList.push(`/${fileInfo.path}`));
Expand Down
Loading