Skip to content
This repository has been archived by the owner on Aug 6, 2024. It is now read-only.

Commit

Permalink
Fix upload command (#62)
Browse files Browse the repository at this point in the history
Fixes #61
  • Loading branch information
svenmuennich authored May 7, 2024
1 parent 08e85ed commit 4876b8d
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 64 deletions.
103 changes: 57 additions & 46 deletions lib/commands/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Program
.option('-R, --no-release', 'Set this option to not submit the uploaded binary for review.')
.option(
'--store-ioncube-encode <on|off|auto>',
'Whether the Store should automatically ionCube-encode the released binary (default is \'auto\', which retains previous release\'s setting)',
'Deprecated: Shopware no longer supports ionCube encryption.',
parseOnOffAutoOption,
'auto',
)
Expand Down Expand Up @@ -64,6 +64,9 @@ if (typeof Program.opts().username === 'undefined') {

async function main() {
const pluginZipFilePath = path.resolve(process.cwd(), Program.args[0]);
if (Program.opts().storeIoncubeEncode !== undefined) {
console.error(Chalk.yellow.bold('Warning: Option \'--store-ioncube-encode\' is deprecated because Shopware no longer supports ionCube encryption. Ignoring.'));
}
try {
const password = await getPassword(Program);
const commander = new ShopwareStoreCommander(Program.opts().username, password);
Expand Down Expand Up @@ -100,15 +103,6 @@ async function main() {
);
const latestReleasedBinary = (releasedBinaries.length > 0) ? releasedBinaries[0] : null;

if (Program.opts().storeIoncubeEncode === 'auto') {
// Set the option based on the latest released binary, if possible
if (latestReleasedBinary) {
Program.opts().storeIoncubeEncode = latestReleasedBinary.ionCubeEncrypted;
} else {
Program.opts().storeIoncubeEncode = false;
console.error(Chalk.yellow.bold('Warning: Cannot automatically determine value for option \'--store-ioncube-encode\', because no valid, released binary exists which it could have been derived from. Using \'false\' instead.'));
}
}
if (Program.opts().licenseCheckRequired === 'auto') {
// Set the option based on the latest released binary, if possible
if (latestReleasedBinary) {
Expand All @@ -119,31 +113,15 @@ async function main() {
}
}

// Make sure that the version of the passed binary does not exist yet
const conflictingBinary = remotePlugin.binaries.find(
binary => binary.version.length > 0 && binary.version === localPlugin.version,
);
if (conflictingBinary) {
if (Program.opts().force) {
remotePlugin = await commander.updatePluginBinary(remotePlugin, conflictingBinary, pluginZipFilePath);
} else {
throw new Error(`The binary version ${conflictingBinary.version} you're trying to upload already exists for plugin ${remotePlugin.name}`);
}
} else {
// Upload the binary
remotePlugin = await commander.uploadPluginBinary(remotePlugin, pluginZipFilePath);
}

// Update the uploaded binary using the plugin info
console.error(`Set version to ${(localPlugin.version)}`);
remotePlugin.latestBinary.version = localPlugin.version;
remotePlugin.latestBinary.changelogs.forEach((changelog) => {
const lang = changelog.locale.name.split('_').shift();
console.error(`Set changelog for language '${lang}'`);
console.error(`Setting version to ${(localPlugin.version)}...`);
const { supportedLocales } = await commander.getAccountData();
const changelogs = supportedLocales.map((locale) => {
const language = locale.split('_').shift();
console.error(`Preparing changelog for language '${language}'...`);
// Try to find a changelog
let changelogText = '';
try {
changelogText = localPlugin.releaseNotes[lang].toHtml();
changelogText = localPlugin.releaseNotes[language].toHtml();
} catch (e) {
console.error(Chalk.yellow.bold(`\u{26A0} ${e.message}`));
}
Expand All @@ -154,25 +132,58 @@ async function main() {
changelogText += '\u{0020}';
}

changelog.text = changelogText;
return {
locale,
text: changelogText,
};
});
const shopwareVersions = commander.statics.softwareVersions;
remotePlugin.latestBinary.compatibleSoftwareVersions = shopwareVersions.filter(
version => version.selectable && localPlugin.isCompatibleWithShopwareVersion(version.name),
);
const compatibleVersionStrings = remotePlugin.latestBinary.compatibleSoftwareVersions.map(version => version.name);
if (compatibleVersionStrings.length > 0) {
console.error(`Set shopware version compatibility: ${compatibleVersionStrings.join(', ')}`);
const compatibleShopwareVersions = commander.statics.softwareVersions
.filter(version => version.selectable && localPlugin.isCompatibleWithShopwareVersion(version.name))
.map(version => version.name);
if (compatibleShopwareVersions.length > 0) {
console.error(`Setting shopware version compatibility: ${compatibleShopwareVersions.join(', ')}`);
} else {
console.error(
Chalk.yellow.bold('\u{26A0} Warning: The plugin\'s compatibility constraints don\'t match any available shopware versions!'),
);
}
remotePlugin.latestBinary.ionCubeEncrypted = Program.opts().storeIoncubeEncode;
remotePlugin.latestBinary.licenseCheckRequired = Program.opts().licenseCheckRequired;
remotePlugin = await commander.savePluginBinary(remotePlugin, remotePlugin.latestBinary);

const uploadSuccessMessage = `New version ${remotePlugin.latestBinary.version} of plugin ${remotePlugin.name} uploaded! \u{2705}`;
// Make sure that the version of the passed binary does not exist yet
const conflictingBinary = remotePlugin.binaries.find(
binary => binary.version.length > 0 && binary.version === localPlugin.version,
);
let existingBinary;
if (!conflictingBinary) {
await commander.validatePluginBinaryFile(remotePlugin, pluginZipFilePath);
existingBinary = await commander.createPluginBinary(
remotePlugin,
localPlugin.version,
changelogs,
compatibleShopwareVersions,
);
} else if (Program.opts().force) {
existingBinary = conflictingBinary;
} else {
throw new Error(`The binary version ${conflictingBinary.version} you're trying to upload already exists for plugin ${remotePlugin.name}`);
}

let updatedBinary = await commander.uploadPluginBinaryFile(
remotePlugin,
existingBinary,
pluginZipFilePath,
);

// Always update the binary after uploading to set the licenseCheckRequired flag because it is not settable
// during creation
updatedBinary = await commander.updatePluginBinary(
remotePlugin,
updatedBinary,
changelogs,
compatibleShopwareVersions,
Program.opts().licenseCheckRequired,
);

const uploadSuccessMessage = `New version ${updatedBinary.version} of plugin ${remotePlugin.name} uploaded! \u{2705}`;
console.error(Chalk.green.bold(uploadSuccessMessage));
if (!Program.opts().release) {
util.showGrowlIfEnabled(uploadSuccessMessage);
Expand All @@ -186,10 +197,10 @@ async function main() {
// Check review status
const review = remotePlugin.reviews[remotePlugin.reviews.length - 1];
if (review.status.name !== 'approved') {
throw new Error(`The review of ${remotePlugin.name} v${remotePlugin.latestBinary.version} finished with status '${review.status.name}':\n\n${review.comment}`);
throw new Error(`The review of ${remotePlugin.name} v${updatedBinary.version} finished with status '${review.status.name}':\n\n${review.comment}`);
}

const successMessage = `Review succeeded! Version ${remotePlugin.latestBinary.version} of plugin ${remotePlugin.name} is now available in the store. \u{1F389}`;
const successMessage = `Review succeeded! Version ${updatedBinary.version} of plugin ${remotePlugin.name} is now available in the store. \u{1F389}`;
util.showGrowlIfEnabled(successMessage);
console.error(Chalk.green.bold(successMessage));

Expand Down
111 changes: 93 additions & 18 deletions lib/shopwareStoreCommander.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ module.exports = class ShopwareStoreCommander {
// Save producer ID (required to load e.g. plugins)
this.accountData = {
producerId: producers.data[0].id,
supportedLocales: producers.data[0].supportedLanguages.map(language => language.name),
};

const plugins = await this.client.get('plugins', {
Expand Down Expand Up @@ -126,21 +127,82 @@ module.exports = class ShopwareStoreCommander {

/**
* @param {Object} plugin
* @param {String} filePath
* @param {String} version
* @param {Array} changelogs
* @param {Array} compatibleShopwareVersions
* @return {Object}
*/
async uploadPluginBinary(plugin, filePath) {
const binaryName = filePath.split(/(\\|\/)/g).pop();
this.logEmitter.emit('info', `Uploading binary ${binaryName} for plugin ${plugin.name}...`);
async createPluginBinary(plugin, version, changelogs, compatibleShopwareVersions) {
this.logEmitter.emit('info', `Creating binary version ${version} of plugin ${plugin.name}...`);

const { data, headers } = await getPostDataFromFile(filePath);
const res = await this.client.post(`plugins/${plugin.id}/binaries`, data, { headers });
const accountData = await this.getAccountData();
const { data: newBinary } = await this.client.post(
`producers/${accountData.producerId}/plugins/${plugin.id}/binaries`,
{
version,
changelogs,
softwareVersions: compatibleShopwareVersions,
},
);

// Add the binary info to the plugin
plugin.binaries = res.data;
const matchingBinaryIndex = plugin.binaries.findIndex(existingBinary => existingBinary.id === newBinary.id);
if (matchingBinaryIndex === -1) {
plugin.binaries.push(newBinary);
} else {
plugin.binaries[matchingBinaryIndex] = newBinary;
}
plugin.latestBinary = plugin.binaries[plugin.binaries.length - 1];

return plugin;
return newBinary;
}

/**
* @param {Object} plugin
* @param {Object} binary
* @param {Array} changelogs
* @param {Array} compatibleShopwareVersions
* @param {Boolean} licenseCheckRequired
* @return {Object}
*/
async updatePluginBinary(plugin, binary, changelogs, compatibleShopwareVersions, licenseCheckRequired) {
this.logEmitter.emit('info', `Updating binary version ${binary.version} of plugin ${plugin.name}...`);

const accountData = await this.getAccountData();
const { data: updatedBinary } = await this.client.put(
`producers/${accountData.producerId}/plugins/${plugin.id}/binaries/${binary.id}`,
{
changelogs,
softwareVersions: compatibleShopwareVersions,
licenseCheckRequired,
// ionCube encryption is no longer supported
ionCubeEncrypted: false,
},
);

const matchingBinaryIndex = plugin.binaries.findIndex(existingBinary => existingBinary.id === binary.id);
if (matchingBinaryIndex !== -1) {
plugin.binaries[matchingBinaryIndex] = updatedBinary;
plugin.latestBinary = plugin.binaries[plugin.binaries.length - 1];
}

return updatedBinary;
}

/**
* @param {Object} plugin
* @param {String} filePath
*/
async validatePluginBinaryFile(plugin, filePath) {
const binaryName = filePath.split(/(\\|\/)/g).pop();
this.logEmitter.emit('info', `Validating binary ${binaryName} for plugin ${plugin.name}...`);

const { data, headers } = await getPostDataFromFile(filePath);
const accountData = await this.getAccountData();
await this.client.post(
`producers/${accountData.producerId}/plugins/${plugin.id}/binaries/validate`,
data,
{ headers },
);
}

/**
Expand All @@ -149,18 +211,25 @@ module.exports = class ShopwareStoreCommander {
* @param {String} filePath
* @return {Object}
*/
async updatePluginBinary(plugin, binary, filePath) {
async uploadPluginBinaryFile(plugin, binary, filePath) {
const binaryName = filePath.split(/(\\|\/)/g).pop();
this.logEmitter.emit('info', `Uploading updated binary ${binaryName} for plugin ${plugin.name}...`);
this.logEmitter.emit('info', `Uploading binary ${binaryName} for plugin ${plugin.name}...`);

const { data, headers } = await getPostDataFromFile(filePath);
const res = await this.client.post(`plugins/${plugin.id}/binaries/${binary.id}/file`, data, { headers });
const accountData = await this.getAccountData();
const { data: updatedBinary } = await this.client.post(
`producers/${accountData.producerId}/plugins/${plugin.id}/binaries/${binary.id}/file`,
data,
{ headers },
);

// Add the binary info to the plugin
plugin.binaries = res.data;
plugin.latestBinary = plugin.binaries[plugin.binaries.length - 1];
const matchingBinaryIndex = plugin.binaries.findIndex(existingBinary => existingBinary.id === binary.id);
if (matchingBinaryIndex !== -1) {
plugin.binaries[matchingBinaryIndex] = updatedBinary;
plugin.latestBinary = plugin.binaries[plugin.binaries.length - 1];
}

return plugin;
return updatedBinary;
}

/**
Expand All @@ -171,7 +240,11 @@ module.exports = class ShopwareStoreCommander {
async savePluginBinary(plugin, binary) {
this.logEmitter.emit('info', `Saving binary version ${binary.version} of plugin ${plugin.name}...`);

const res = await this.client.put(`plugins/${plugin.id}/binaries/${binary.id}`, binary);
const accountData = await this.getAccountData();
const res = await this.client.put(
`producers/${accountData.producerId}/plugins/${plugin.id}/binaries/${binary.id}`,
binary,
);
// Save the updated data locally
binary.changelogs = res.data.changelogs;
binary.compatibleSoftwareVersions = res.data.compatibleSoftwareVersions;
Expand Down Expand Up @@ -274,8 +347,10 @@ module.exports = class ShopwareStoreCommander {
async loadExtraPluginFields(plugin, fields) {
plugin.scsLoadedExtraFields = plugin.scsLoadedExtraFields || [];
// Load all extra fields
const accountData = await this.getAccountData();
const extraFieldPromises = fields.map(async (field) => {
const res = await this.client.get(`plugins/${plugin.id}/${field}`);
const path = (field === 'binaries') ? `producers/${accountData.producerId}/plugins/${plugin.id}/${field}` : `plugins/${plugin.id}/${field}`;
const res = await this.client.get(path);
plugin[field] = res.data;
// Mark the extra field as loaded
if (plugin.scsLoadedExtraFields.indexOf(field) === -1) {
Expand Down

0 comments on commit 4876b8d

Please sign in to comment.