Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/sc 133459/resume downloads from s3 #788

Merged
merged 42 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
21c3919
Add support to setup Tachyon
keeramis Jan 24, 2025
ce60b83
Refactor
keeramis Jan 24, 2025
09115dd
Ensure interfaces are consistent and refine existing steps
keeramis Jan 24, 2025
f3d35f7
Crypto fix
keeramis Jan 24, 2025
9b6b7b1
No need to wrap crypto512 output
monkbroc Jan 24, 2025
7aac3cd
add tmp channels
hugomontero Jan 22, 2025
55df79f
add node-fetch to download binaries
hugomontero Jan 23, 2025
916b431
implement download manager to resume interrupted downloads
hugomontero Jan 23, 2025
e849cf7
use ui.write and error instead of console
hugomontero Jan 23, 2025
2b0502e
add right url and main channel for download binaries
hugomontero Jan 24, 2025
da774fb
add checksum validation
hugomontero Jan 24, 2025
5b2052d
change test strategy for tracking errors
hugomontero Jan 24, 2025
7dbe8ea
fix checksum validation
hugomontero Jan 24, 2025
6699fdf
implement cached files
hugomontero Jan 24, 2025
011536a
remove tmp dir and use .progress sufix to indicate is in progress
hugomontero Jan 24, 2025
f9a93d3
refactor checksum validation and progress files
hugomontero Jan 24, 2025
815c4dd
Integrate image download into Tachyon setup
monkbroc Jan 24, 2025
f8ab4d7
use right fetchManifest function and add latest as default version
hugomontero Jan 24, 2025
5cdbe38
Include Tachyon in device constants
monkbroc Jan 24, 2025
e781a15
Fixes for Tachyon setup
monkbroc Jan 24, 2025
c5c4805
add command download-package
hugomontero Jan 24, 2025
1a54961
fix progressbar when resuming download
hugomontero Jan 24, 2025
b8431d6
add await to checksum validation and put an spinner to wait
hugomontero Jan 24, 2025
052d6c1
Pass downloaded URL to flash command
monkbroc Jan 25, 2025
8cbeac0
Add in text to guide the setup process
mrlambchop Jan 25, 2025
85008f5
Update
mrlambchop Jan 25, 2025
46a1780
Fix download URL of PR versions
monkbroc Jan 25, 2025
ad42abe
Fix org / sandbox
mrlambchop Jan 25, 2025
79a75fe
Add in CLI params
mrlambchop Jan 25, 2025
ecd87d2
Update message
mrlambchop Jan 26, 2025
378c8b2
Add in password confirmation
mrlambchop Jan 27, 2025
081d6aa
Add in better error handling
mrlambchop Jan 27, 2025
68b40ef
fix tests by adding spinner stub
hugomontero Jan 27, 2025
5f3eba3
add cleanUp cache command
hugomontero Jan 27, 2025
d4bb224
Minor cleanup
keeramis Jan 27, 2025
a6ae2d7
add resilience to download method, wait, retry and continue after con…
hugomontero Jan 27, 2025
e107667
fix destructuring
keeramis Jan 27, 2025
58d34b2
fix Typo
keeramis Jan 27, 2025
8e89478
refactor download method
hugomontero Jan 28, 2025
99392c4
Merge branch 'feature/sc-133459/resume-downloads-from-s3' of github.c…
hugomontero Jan 28, 2025
a1346f8
fix tests after updating device-constants
hugomontero Jan 28, 2025
3d08aa3
fix e2e tests
hugomontero Jan 28, 2025
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
Binary file modified assets/qdl/darwin/arm64/qdl
Binary file not shown.
Binary file modified assets/qdl/darwin/x64/qdl
Binary file not shown.
Binary file modified assets/qdl/linux/arm64/qdl
Binary file not shown.
Binary file modified assets/qdl/linux/x64/qdl
Binary file not shown.
Binary file modified assets/qdl/win32/x64/qdl.exe
Binary file not shown.
1,290 changes: 1,094 additions & 196 deletions npm-shrinkwrap.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
}
],
"dependencies": {
"@particle/device-constants": "^3.7.0",
"@particle/device-constants": "^3.8.0",
"binary-version-reader": "^2.5.1",
"chalk": "^2.4.2",
"cli-progress": "^3.12.0",
Expand All @@ -66,6 +66,7 @@
"jose": "^4.13.1",
"lodash": "^4.17.15",
"moment": "^2.24.0",
"node-fetch": "^2.7.0",
"node-wifiscanner2": "^1.2.1",
"particle-api-js": "^11.1.0",
"particle-commands": "^1.0.1",
Expand All @@ -75,6 +76,7 @@
"safe-buffer": "^5.2.0",
"semver": "^7.5.2",
"serialport": "^10.4.0",
"sha512crypt-node": "^1.0.2",
"softap-setup": "^4.1.0",
"temp": "^0.9.1",
"unzipper": "^0.12.3",
Expand Down
4 changes: 3 additions & 1 deletion settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ let settings = {

cloudKnownApps: {
'tinker': true
}
},

tachyonMeta: 'https://tachyon-ci.particle.io/meta'
};

function envValue(varName, defaultValue) {
Expand Down
4 changes: 2 additions & 2 deletions src/cli/cloud.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('Cloud Command-Line Interface', () => {
'Display a list of your devices, as well as their variables and functions',
'Usage: particle cloud list [options] [filter]',
'',
'Param filter can be: online, offline, a platform name (core, photon, p1, electron, argon, boron, xenon, esomx, bsom, b5som, tracker, trackerm, p2, msom, electron2), a device ID or name',
'Param filter can be: online, offline, a platform name (core, photon, p1, electron, argon, boron, xenon, esomx, bsom, b5som, tracker, trackerm, p2, msom, electron2, tachyon), a device ID or name',
''
].join('\n'));
});
Expand Down Expand Up @@ -308,7 +308,7 @@ describe('Cloud Command-Line Interface', () => {
' particle cloud compile photon Compile the source code in the current directory in the cloud for a `photon`',
' particle cloud compile electron project --saveTo electron.bin Compile the source code in the project directory in the cloud for an `electron` and save it to a file named `electron.bin`',
'',
'Param deviceType can be: core, c, photon, p, p1, electron, e, argon, a, boron, b, xenon, x, esomx, bsom, b5som, tracker, assettracker, trackerm, p2, photon2, msom, muon, electron2',
'Param deviceType can be: core, c, photon, p, p1, electron, e, argon, a, boron, b, xenon, x, esomx, bsom, b5som, tracker, assettracker, trackerm, p2, photon2, msom, muon, electron2, tachyon',
''
].join('\n'));
});
Expand Down
2 changes: 2 additions & 0 deletions src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const webhook = require('./webhook');
const whoami = require('./whoami');
const wifi = require('./wifi');
const usb = require('./usb');
const tachyon = require('./tachyon');

/**
* The default function export from this module registers all the available commands.
Expand Down Expand Up @@ -64,6 +65,7 @@ module.exports = function registerAllCommands(context) {
publish(context);
serial(context);
subscribe(context);
tachyon(context);
token(context);
udp(context);
updateCli(context);
Expand Down
80 changes: 80 additions & 0 deletions src/cli/tachyon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
module.exports = ({ commandProcessor, root }) => {
const tachyon = commandProcessor.createCategory(root, 'tachyon', 'Setup Particle devices');

commandProcessor.createCommand(tachyon, 'setup', 'Setup a Tachyon device', {
options: {
skip_flashing_os: {
description: 'Skip flashing the Operating System',
boolean: true
},
version: {
description: 'Version to download package for (default: latest). Can include a directory or a local zip file',
type: 'string'
},
load_config: {
description: 'Path to a config file to use for setup',
type: 'string'
},
save_config: {
description: 'Path to dump the config file to after setup',
type: 'string'
}
},
handler: (args) => {
const SetupTachyonCommands = require('../cmd/setup-tachyon');
return new SetupTachyonCommands().setup(args);
},
examples: {
'$0 $command': 'Setup a Tachyon device'
}
});

commandProcessor.createCommand(tachyon, 'download-package', 'Download a Tachyon package', {
options: {
region: {
description: 'Region to download package for',
type: 'string',
},
version: {
description: 'Version to download package for',
type: 'string'
}
},
handler: (args) => {
const DownloadTachyonPackageCommand = require('../cmd/download-tachyon-package');
return new DownloadTachyonPackageCommand().download(args);
},
examples: {
'$0 $command --region': 'Download a Tachyon package for the US region and version 1.0.0',
'$0 $command --region NA --version 1.0.0': 'Download a Tachyon package for the North America region and version 1.0.0'
}
});

commandProcessor.createCommand(tachyon, 'clean-cache', 'Clean the Tachyon package cache', {
options: {
region: {
description: 'Region to download package for',
type: 'string',
},
version: {
description: 'Version to download package for',
type: 'string'
},
all: {
description: 'Clean all cached packages',
boolean: true
}
},
handler: (args) => {
const CleanPackageCacheCommand = require('../cmd/download-tachyon-package');
return new CleanPackageCacheCommand().cleanUp(args);
},
examples: {
'$0 $command --region NA --version 1.0.0': 'Clean the Tachyon package cache for the North America region and version 1.0.0',
'$0 $command --all': 'Clean all cached packages',
}
});

return tachyon;
};

2 changes: 1 addition & 1 deletion src/cli/usb.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('USB Command-Line Interface', () => {
' --exclude-dfu Do not list devices which are in DFU mode [boolean]',
' --ids-only Print only device IDs [boolean]',
'',
'Param filter can be: online, offline, a platform name (core, photon, p1, electron, argon, boron, xenon, esomx, bsom, b5som, tracker, trackerm, p2, msom, electron2), a device ID or name',
'Param filter can be: online, offline, a platform name (core, photon, p1, electron, argon, boron, xenon, esomx, bsom, b5som, tracker, trackerm, p2, msom, electron2, tachyon), a device ID or name',
''
].join('\n'));
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/wifi.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const unindent = require('../lib/unindent');

module.exports = ({ commandProcessor, root }) => {
const wifi = commandProcessor.createCategory(root, 'wifi', 'Configure Wi-Fi credentials to your device (Supported on Gen 3+ devices).');
const wifi = commandProcessor.createCategory(root, 'wifi', 'Configure Wi-Fi credentials to your device (Supported on Gen 3+ devices)');

commandProcessor.createCommand(wifi, 'add', 'Adds a Wi-Fi network to your device', {
options: Object.assign({
Expand Down
37 changes: 37 additions & 0 deletions src/cmd/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,43 @@ module.exports = class ParticleApi {
);
}

getOrgs() {
return this._wrap(
this.api.get({
uri: '/v1/orgs',
auth: this.accessToken
})
);
}

getRegistrationCode(productId) {
return this._wrap(
this.api.post({
uri: `/v1/products/${productId}/registration_code`,
auth: this.accessToken
})
);
}

createProduct({ name, type, org }) {
return this._wrap(
this.api.post({
uri: `/v1${org ? `/orgs/${org}` : ''}/products`,
form: { name, type },
auth: this.accessToken
})
);
}

getProducts(org) {
return this._wrap(
this.api.get({
uri: `/v1${org ? `/orgs/${org}` : ''}/products`,
auth: this.accessToken
})
);
}

getDevice({ deviceId: id }) {
return this.api.getDevice({ deviceId: id, auth: this.accessToken });
}
Expand Down
84 changes: 84 additions & 0 deletions src/cmd/download-tachyon-package.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const CLICommandBase = require('./base');
const DownloadManager = require('../lib/download-manager');

module.exports = class DownloadTachyonPackageCommand extends CLICommandBase {
constructor({ ui } = {}) {
super();
this.ui = ui || this.ui;
}

async _selectRegion() {
const regionMapping = {
'NA (North America)': 'NA',
'RoW (Rest of the World)': 'RoW'
};
const question = [
{
type: 'list',
name: 'region',
message: 'Select the region:',
choices: Object.keys(regionMapping),
},
];
const { region } = await this.ui.prompt(question);
return regionMapping[region];
}

async _selectVersion() {
const question = [
{
type: 'input',
name: 'version',
message: 'Enter the version number:',
default: 'latest',
},
];
const answer = await this.ui.prompt(question);
return answer.version;
}
async download ({ region, version }) {
// prompt for region and version if not provided
if (!region) {
region = await this._selectRegion();
}
if (!version) {
version = await this._selectVersion();
}
const manager = new DownloadManager(this.ui);
const manifest = await manager.fetchManifest({ version });
const build = manifest.builds.find((b) => b.region === region);
if (!build) {
throw new Error(`No build available for region: ${region}`);
}
const { artifact_url: url, sha256_checksum: expectedChecksum } = build.artifacts[0];
const outputFileName = url.replace(/.*\//, '');
const filePath = await manager.download({ url, outputFileName, expectedChecksum });
this.ui.write(`Downloaded package to: ${filePath}`);

return filePath;
}

async cleanUp({ region, version, all }) {
const manager = new DownloadManager(this.ui);
if (all) {
await manager.cleanup({ cleanDownload: true, cleanInProgress: true });
this.ui.write('Cleaned up all cached packages');
} else {
if (!region) {
region = await this._selectRegion();
}
if (!version) {
version = await this._selectVersion();
}
const manifest = await manager.fetchManifest({ version });
const build = manifest.builds.find((b) => b.region === region);
if (!build) {
throw new Error(`No build available for region: ${region}`);
}
const { artifact_url: url } = build.artifacts[0];
const outputFileName = url.replace(/.*\//, '');
await manager.cleanup({ cleanDownload: false, fileName: outputFileName });
this.ui.write(`Cleaned up package cache for region: ${region} and version: ${version}`);
}
}
};
11 changes: 6 additions & 5 deletions src/cmd/flash.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,8 @@ module.exports = class FlashCommand extends CLICommandBase {
}
}

//returns true if successful or false if failed
async flashTachyon({ files, output }) {
this.ui.write(`${os.EOL}Ensure that only one device is connected to the computer before proceeding.${os.EOL}`);

let zipFile;
let includeDir = '';
let updateFolder = '';
Expand All @@ -82,7 +81,7 @@ module.exports = class FlashCommand extends CLICommandBase {
files = ['.'];
}

const input = files[0];
const [input, ...rest] = files;
const stats = await fs.stat(input);
let filesToProgram;

Expand All @@ -97,17 +96,17 @@ module.exports = class FlashCommand extends CLICommandBase {
const zipInfo = await this._extractFlashFilesFromZip(input);
includeDir = zipInfo.baseDir;
filesToProgram = zipInfo.filesToProgram.map((file) => path.join(includeDir, file));
filesToProgram.push(...rest);
} else {
filesToProgram = files;
}

this.ui.write(`Starting download. This may take several minutes...${os.EOL}`);
if (output && !fs.existsSync(output)) {
fs.mkdirSync(output);
}
const outputLog = path.join(output ? output : process.cwd(), `tachyon_flash_${Date.now()}.log`);
try {
this.ui.write(`Logs are being written to: ${outputLog}${os.EOL}`);
this.ui.write(`Starting download. This may take several minutes. See logs at: ${outputLog}${os.EOL}`);
const qdl = new QdlFlasher({
files: filesToProgram,
includeDir,
Expand All @@ -118,9 +117,11 @@ module.exports = class FlashCommand extends CLICommandBase {
});
await qdl.run();
fs.appendFileSync(outputLog, 'Download complete.');
return true;
} catch (error) {
this.ui.write('Download failed');
fs.appendFileSync(outputLog, 'Download failed with error: ' + error.message);
return false;
}
}

Expand Down
Loading