Skip to content
Draft
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
133 changes: 132 additions & 1 deletion packages/cli/src/lib/setup/setupInstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1681,6 +1681,76 @@ export class Install {
}
}

/**
* Fetch and validate io-package.json from GitHub before installation
*
* @param user GitHub username
* @param repo GitHub repository name
* @param commit Commit hash or branch name
* @returns Object with io-package data if fetched, or null if not available
*/
private async _fetchAndValidateIoPackage(
user: string,
repo: string,
commit: string,
): Promise<ioBroker.AdapterObject | null> {
try {
// Try to fetch io-package.json from GitHub using the commit hash
const ioPackageUrl = `https://raw.githubusercontent.com/${user}/${repo}/${commit}/io-package.json`;
const result = await axios(ioPackageUrl, {
headers: {
'User-Agent': 'ioBroker Adapter install',
},
timeout: 10000,
validateStatus: status => status === 200,
});

if (result.data && result.data.common) {
return result.data as ioBroker.AdapterObject;
}
return null;
} catch {
// Could not fetch io-package.json
return null;
}
}

/**
* Perform adapter compatibility checks using io-package data
*
* @param ioPackage The io-package.json data
* @param repoName The repository name for error messages
* @returns true if checks pass, false if installation should be blocked
*/
private _performAdapterChecks(ioPackage: ioBroker.AdapterObject, repoName: string): boolean {
// Check nogit flag
if (ioPackage.common.nogit === true) {
console.error(
`Cannot install adapter from GitHub: The adapter "${repoName}" has set the "nogit" flag which prevents manual installation from GitHub.`,
);
return false;
}

// Check OS compatibility
if (ioPackage.common.os) {
if (typeof ioPackage.common.os === 'string' && ioPackage.common.os !== osPlatform) {
console.error(
`Cannot install adapter from GitHub: Adapter does not support current OS. Required ${ioPackage.common.os}. Actual platform: ${osPlatform}`,
);
return false;
} else if (Array.isArray(ioPackage.common.os) && !ioPackage.common.os.includes(osPlatform as any)) {
console.error(
`Cannot install adapter from GitHub: Adapter does not support current OS. Required one of ${ioPackage.common.os.join(
', ',
)}. Actual platform: ${osPlatform}`,
);
return false;
}
}

return true;
}

/**
* Installs an adapter from given url
*
Expand All @@ -1698,6 +1768,10 @@ export class Install {

const debug = process.argv.includes('--debug');

let githubUser: string | undefined;
let githubRepo: string | undefined;
let githubCommit: string | undefined;

if (parsedUrl && parsedUrl.hostname === 'github.com') {
if (!tools.isGithubPathname(parsedUrl.pathname)) {
return console.error(`Cannot install from GitHub. Invalid URL ${url}`);
Expand All @@ -1707,6 +1781,9 @@ export class Install {
// @ts-expect-error check if type check above is enough
const { repo, user, commit } = tools.parseGithubPathname(parsedUrl.pathname);

githubUser = user;
githubRepo = repo;

if (!commit) {
// No commit given, try to get it from the API
try {
Expand All @@ -1718,7 +1795,8 @@ export class Install {
},
});
if (result.data && Array.isArray(result.data) && result.data.length >= 1 && result.data[0].sha) {
url = `${user}/${repo}#${result.data[0].sha}`;
githubCommit = result.data[0].sha;
url = `${user}/${repo}#${githubCommit}`;
} else {
console.log(
`Info: Can not get current GitHub commit, only remember that we installed from GitHub.`,
Expand All @@ -1734,8 +1812,61 @@ export class Install {
}
} else {
// We've extracted all we need from the URL
githubCommit = commit;
url = `${user}/${repo}#${commit}`;
}

// Fetch and validate io-package if we have the commit hash
if (githubCommit && githubUser && githubRepo) {
const ioPackage = await this._fetchAndValidateIoPackage(githubUser, githubRepo, githubCommit);

if (ioPackage) {
// Perform all adapter compatibility checks
if (!this._performAdapterChecks(ioPackage, githubRepo)) {
return;
}
} else {
console.warn(
`Warning: Could not verify adapter compatibility from GitHub. Installation will proceed but may fail if the adapter does not support GitHub installation.`,
);
}
} else if (githubUser && githubRepo) {
// We have GitHub info but no commit hash, cannot verify compatibility
console.warn(
`Warning: Could not determine commit hash from GitHub. Installation will proceed but may fail if the adapter does not support GitHub installation.`,
);
}
}

// Also check short GitHub URL format if not already checked
if (!githubUser && !githubRepo) {
const shortGithubUrlParts = tools.parseShortGithubUrl(url);
if (shortGithubUrlParts) {
githubUser = shortGithubUrlParts.user;
githubRepo = shortGithubUrlParts.repo;
githubCommit = shortGithubUrlParts.commit;

// Fetch and validate io-package if we have the commit hash
if (githubCommit && githubUser && githubRepo) {
const ioPackage = await this._fetchAndValidateIoPackage(githubUser, githubRepo, githubCommit);

if (ioPackage) {
// Perform all adapter compatibility checks
if (!this._performAdapterChecks(ioPackage, githubRepo)) {
return;
}
} else {
console.warn(
`Warning: Could not verify adapter compatibility from GitHub. Installation will proceed but may fail if the adapter does not support GitHub installation.`,
);
}
} else if (githubUser && githubRepo) {
// We have GitHub info but no commit hash, cannot verify compatibility
console.warn(
`Warning: Could not determine commit hash from GitHub. Installation will proceed but may fail if the adapter does not support GitHub installation.`,
);
}
}
}

console.log(`install ${url}`);
Expand Down