Skip to content

Commit

Permalink
Merge branch 'ink-devhub-1' into feature/faucet
Browse files Browse the repository at this point in the history
  • Loading branch information
ipapandinas authored Feb 12, 2024
2 parents 619e39a + cd93607 commit 54f1672
Show file tree
Hide file tree
Showing 19 changed files with 2,235 additions and 1,384 deletions.
6 changes: 4 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"name": "swanky-env",
"image": "ghcr.io/swankyhub/swanky-cli/swanky-base:swanky3.1.0-beta.0_v2.1.0",

"image": "ghcr.io/inkdevhub/swanky-cli/swanky-base:swanky3.1.0-beta.0_v2.1.1",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2.8.0": {}
},
// Mount the workspace volume
"mounts": ["source=${localWorkspaceFolder},target=/workspaces,type=bind,consistency=cached"],
"workspaceFolder": "/workspaces",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ A newly generated project will have a `swanky.config.json` file that will get po
"node": {
"localPath": "/Users/sasapul/Work/astar/swanky-cli/temp_proj/bin/swanky-node",
"polkadotPalletVersions": "polkadot-v0.9.39",
"supportedInk": "v4.2.0"
"supportedInk": "v4.3.0"
},
"accounts": [
{
Expand Down
12 changes: 6 additions & 6 deletions base-image/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ RUN curl -L https://github.com/swankyhub/swanky-cli/releases/download/v3.1.0-bet
# Install Rustup and Rust, additional components, packages, and verify the installations
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \
/bin/bash -c "source $HOME/.cargo/env && \
rustup toolchain install nightly-2023-03-05 && \
rustup default nightly-2023-03-05 && \
rustup component add rust-src --toolchain nightly-2023-03-05 && \
rustup target add wasm32-unknown-unknown --toolchain nightly-2023-03-05 && \
cargo +stable install cargo-dylint dylint-link && \
cargo +stable install cargo-contract --force --version 4.0.0-alpha && \
rustup install 1.72 && \
rustup default 1.72 && \
rustup component add rust-src && \
rustup target add wasm32-unknown-unknown && \
cargo install cargo-dylint dylint-link && \
cargo install cargo-contract --version 4.0.0-rc.1 && \
rustc --version"

# Install Yarn 1.x
Expand Down
210 changes: 182 additions & 28 deletions src/commands/check/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { Listr } from "listr2";
import { commandStdoutOrNull } from "../../lib/index.js";
import { commandStdoutOrNull, extractCargoContractVersion } from "../../lib/index.js";
import { SwankyConfig } from "../../types/index.js";
import { pathExistsSync, readJSON } from "fs-extra/esm";
import { pathExistsSync, readJSON, writeJson } from "fs-extra/esm";
import { readFileSync } from "fs";
import path from "node:path";
import TOML from "@iarna/toml";
import semver from "semver";
import { SwankyCommand } from "../../lib/swankyCommand.js";
import { Flags } from "@oclif/core";
import chalk from "chalk";
import { CARGO_CONTRACT_INK_DEPS } from "../../lib/cargoContractInfo.js";
import { CLIError } from "@oclif/core/lib/errors/index.js";
import Warn = CLIError.Warn;

interface Ctx {
os: {
platform: string;
architecture: string;
},
versions: {
tools: {
rust?: string | null;
Expand All @@ -17,50 +26,116 @@ interface Ctx {
cargoDylint?: string | null;
cargoContract?: string | null;
};
supportedInk?: string;
missingTools: string[];
contracts: Record<string, Record<string, string>>;
swankyNode: string | null;
};
swankyConfig?: SwankyConfig;
mismatchedVersions?: Record<string, string>;
mismatchedVersions: Record<string, string>;
looseDefinitionDetected: boolean;
}

export default class Check extends SwankyCommand<typeof Check> {
static description = "Check installed package versions and compatibility";

static flags = {
print: Flags.string({
char: "o",
description: "File to write output to",
}),
};

public async run(): Promise<void> {
const { flags } = await this.parse(Check);
const swankyNodeVersion = this.swankyConfig.node.version;
const isSwankyNodeInstalled = !!swankyNodeVersion;
const anyContracts = Object.keys(this.swankyConfig?.contracts).length > 0;
const tasks = new Listr<Ctx>([
{
title: "Check OS",
task: async (ctx, task) => {
ctx.os.platform = process.platform;
ctx.os.architecture = process.arch;
const supportedPlatforms = ["darwin", "linux"];
const supportedArch = ["arm64", "x64"];

if (!supportedPlatforms.includes(ctx.os.platform)) {
throw new Error(`Platform ${ctx.os.platform} is not supported`);
}
if (!supportedArch.includes(ctx.os.architecture)) {
throw new Error(`Architecture ${ctx.os.architecture} is not supported`);
}

task.title = `Check OS: '${ctx.os.platform}-${ctx.os.architecture}'`;
},
exitOnError: false,
},
{
title: "Check Rust",
task: async (ctx) => {
ctx.versions.tools.rust = await commandStdoutOrNull("rustc --version");
task: async (ctx, task) => {
ctx.versions.tools.rust = commandStdoutOrNull("rustc --version")?.match(/rustc (.*) \((.*)/)?.[1];
if (!ctx.versions.tools.rust) {
throw new Error("Rust is not installed");
}
task.title = `Check Rust: ${ctx.versions.tools.rust}`;
},
exitOnError: false,
},
{
title: "Check cargo",
task: async (ctx) => {
ctx.versions.tools.cargo = await commandStdoutOrNull("cargo -V");
task: async (ctx, task) => {
ctx.versions.tools.cargo = commandStdoutOrNull("cargo -V")?.match(/cargo (.*) \((.*)/)?.[1];
if (!ctx.versions.tools.cargo) {
throw new Error("Cargo is not installed");
}
task.title = `Check cargo: ${ctx.versions.tools.cargo}`;
},
exitOnError: false,
},
{
title: "Check cargo nightly",
task: async (ctx) => {
ctx.versions.tools.cargoNightly = await commandStdoutOrNull("cargo +nightly -V");
task: async (ctx, task) => {
ctx.versions.tools.cargoNightly = commandStdoutOrNull("cargo +nightly -V")?.match(/cargo (.*)-nightly \((.*)/)?.[1];
if (!ctx.versions.tools.cargoNightly) {
throw new Error("Cargo nightly is not installed");
}
task.title = `Check cargo nightly: ${ctx.versions.tools.cargoNightly}`;
},
exitOnError: false,
},
{
title: "Check cargo dylint",
task: async (ctx) => {
ctx.versions.tools.cargoDylint = await commandStdoutOrNull("cargo dylint -V");
task: async (ctx, task) => {
ctx.versions.tools.cargoDylint = commandStdoutOrNull("cargo dylint -V")?.match(/cargo-dylint (.*)/)?.[1];
if (!ctx.versions.tools.cargoDylint) {
throw new Warn("Cargo dylint is not installed");
}
task.title = `Check cargo dylint: ${ctx.versions.tools.cargoDylint}`;
},
exitOnError: false,
},
{
title: "Check cargo-contract",
task: async (ctx, task) => {
const cargoContractVersion = extractCargoContractVersion();
ctx.versions.tools.cargoContract = cargoContractVersion;
if (!cargoContractVersion) {
throw new Error("Cargo contract is not installed");
}
task.title = `Check cargo-contract: ${cargoContractVersion}`;
},
exitOnError: false,
},
{
title: "Check swanky node",
task: async (ctx) => {
ctx.versions.tools.cargoContract = await commandStdoutOrNull("cargo contract -V");
ctx.versions.swankyNode = this.swankyConfig.node.version !== "" ? this.swankyConfig.node.version : null;
},
},
{
title: "Read ink dependencies",
enabled: anyContracts,
task: async (ctx) => {
const swankyConfig = await readJSON("swanky.config.json");
ctx.swankyConfig = swankyConfig;
Expand All @@ -79,7 +154,7 @@ export default class Check extends SwankyCommand<typeof Check> {
const cargoToml = TOML.parse(cargoTomlString);

const inkDependencies = Object.entries(cargoToml.dependencies)
.filter((dependency) => dependency[0].includes("ink_"))
.filter(([depName]) => /^ink($|_)/.test(depName))
.map(([depName, depInfo]) => {
const dependency = depInfo as Dependency;
return [depName, dependency.version ?? dependency.tag];
Expand All @@ -89,43 +164,122 @@ export default class Check extends SwankyCommand<typeof Check> {
},
},
{
title: "Verify ink version",
title: "Verify ink version compatibility with Swanky node",
skip: (ctx) => Object.keys(ctx.versions.contracts).length === 0,
enabled: anyContracts && isSwankyNodeInstalled,
task: async (ctx) => {
const supportedInk = ctx.swankyConfig?.node.supportedInk;

const supportedInk = ctx.swankyConfig!.node.supportedInk;
const mismatched: Record<string, string> = {};
Object.entries(ctx.versions.contracts).forEach(([contract, inkPackages]) => {
Object.entries(inkPackages).forEach(([inkPackage, version]) => {
if (semver.gt(version, supportedInk!)) {
Object.entries(ctx.versions.contracts).forEach(([contract, inkDependencies]) => {
Object.entries(inkDependencies).forEach(([depName, version]) => {
if (semver.gt(version, supportedInk)) {
mismatched[
`${contract}-${inkPackage}`
] = `Version of ${inkPackage} (${version}) in ${contract} is higher than supported ink version (${supportedInk})`;
`${contract}-${depName}`
] = `Version of ${depName} (${version}) in ${chalk.yellowBright(contract)} is higher than supported ink version (${supportedInk}) in current Swanky node version (${swankyNodeVersion}). A Swanky node update can fix this warning.`;
}

if (!(version.startsWith("=") || version.startsWith("v"))) {
if (version.startsWith(">") || version.startsWith("<") || version.startsWith("^") || version.startsWith("~")) {
ctx.looseDefinitionDetected = true;
}
});
});

ctx.mismatchedVersions = mismatched;
if (Object.entries(mismatched).length > 0) {
throw new Warn("Ink versions in contracts don't match the Swanky node's supported version.");
}
},
exitOnError: false,
},
{
title: "Verify cargo contract compatibility",
skip: (ctx) => !ctx.versions.tools.cargoContract,
enabled: anyContracts,
task: async (ctx) => {
const cargoContractVersion = ctx.versions.tools.cargoContract!;
const dependencyIdx = CARGO_CONTRACT_INK_DEPS.findIndex((dep) =>
semver.satisfies(cargoContractVersion.replace(/-.*$/, ""), `>=${dep.minCargoContractVersion}`)
);

if (dependencyIdx === -1) {
throw new Warn(`cargo-contract version ${cargoContractVersion} is not supported`);
}

const validInkVersionRange = CARGO_CONTRACT_INK_DEPS[dependencyIdx].validInkVersionRange;
const minCargoContractVersion = dependencyIdx === 0
? CARGO_CONTRACT_INK_DEPS[dependencyIdx].minCargoContractVersion
: CARGO_CONTRACT_INK_DEPS[dependencyIdx - 1].minCargoContractVersion

const mismatched: Record<string, string> = {};
Object.entries(ctx.versions.contracts).forEach(([contract, inkPackages]) => {
Object.entries(inkPackages).forEach(([inkPackage, version]) => {
if (!semver.satisfies(version, validInkVersionRange)) {
mismatched[
`${contract}-${inkPackage}`
] = `Version of ${inkPackage} (${version}) in ${chalk.yellowBright(contract)} requires cargo-contract version >=${minCargoContractVersion}, but version ${cargoContractVersion} is installed`;
}
});
});

ctx.mismatchedVersions = { ...ctx.mismatchedVersions, ...mismatched };
if (Object.entries(mismatched).length > 0) {
throw new Warn("cargo-contract version mismatch");
}
},
exitOnError: false,
},
{
title: "Check for missing tools",
task: async (ctx) => {
const missingTools: string[] = [];
for (const [toolName, toolVersion] of Object.entries(ctx.versions.tools)) {
if (!toolVersion) {
missingTools.push(toolName);
if (toolName === "cargoDylint") this.warn("Cargo dylint is not installed");
else this.error(`${toolName} is not installed`);
}
}
ctx.versions.missingTools = missingTools;
if (Object.entries(missingTools).length > 0) {
throw new Warn(`Missing tools: ${missingTools.join(", ")}`);
}
},
exitOnError: false,
},
]);

const context = await tasks.run({
versions: { tools: {}, contracts: {} },
os: { platform: "", architecture: "" },
versions: {
tools: {},
missingTools: [],
contracts: {},
swankyNode: swankyNodeVersion || null,
},
looseDefinitionDetected: false,
mismatchedVersions: {}
});
console.log(context.versions);
Object.values(context.mismatchedVersions as any).forEach((mismatch) =>
console.error(`[ERROR] ${mismatch as string}`)
);

Object.values(context.mismatchedVersions).forEach((mismatch) => this.warn(mismatch));

if (context.looseDefinitionDetected) {
console.log(`\n[WARNING]Some of the ink dependencies do not have a fixed version.
this.warn(`Some of the ink dependencies do not have a fixed version.
This can lead to accidentally installing version higher than supported by the node.
Please use "=" to install a fixed version (Example: "=3.0.1")
`);
}

const output = {
...context.os,
...context.versions
}

const filePath = flags.print;
if (filePath !== undefined) {
await this.spinner.runCommand(async () => {
writeJson(filePath, output, { spaces: 2 });
}, `Writing output to file ${chalk.yellowBright(filePath)}`);
}
}
}

Expand Down
Loading

0 comments on commit 54f1672

Please sign in to comment.