cli: Télécharge et extrait les paquets d'un .zip

This commit is contained in:
2022-10-13 08:31:14 +02:00
parent 74ef6db44f
commit a7125e3acf
20 changed files with 172 additions and 26 deletions

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ oki
# Dependencies
third-party
dependencies

View File

@@ -33,9 +33,8 @@ php -S localhost:8000 -t web/public
### Compile the cli
#### At home
```bash
cd cli
apt-get install -y nlohmann-json3-dev libcurl4-openssl-dev
make
apt-get install -y nlohmann-json3-dev libcurl4-openssl-dev libminizip-dev
cd cli && make
```
#### At the IUT
```bash

View File

@@ -8,7 +8,7 @@ CXXFLAGS := -std=c++20 \
-pedantic \
-g -MMD -MP \
-fdiagnostics-color=always
LDLIBS := -lcurl # Libraries utilisées pour l'édition des liens
LDLIBS := -lcurl -lminizip # Bibliothèques utilisées pour l'édition des liens
# Nom de l'exécutable final
TARGET_EXE := oki

View File

@@ -22,8 +22,8 @@ vdn-ssh root@$VDN_SYSTEM << EOF
route del default gw 192.168.2.1
route add default gw 10.0.2.2
export http_proxy=http://193.49.118.36:8080/
if ! pkg-config nlohmann_json --exists || ! pkg-config libcurl --exists; then
apt-get install -y nlohmann-json3-dev libcurl4-openssl-dev
if ! pkg-config nlohmann_json --exists || ! pkg-config libcurl --exists || ! pkg-config libminizip --exists; then
apt-get install -y nlohmann-json3-dev libcurl4-openssl-dev libminizip-dev
fi
make
EOF

76
cli/src/io/Archive.cpp Normal file
View File

@@ -0,0 +1,76 @@
#include "Archive.h"
#include <minizip/unzip.h>
#include <sys/stat.h>
#include <utility>
#define BUFFER_SIZE 256
#define DIRECTORY_PERMISSIONS 755
// Basé sur https://github.com/madler/zlib/blob/master/contrib/minizip/miniunz.c
namespace oki{
Extractor::Extractor(std::filesystem::path destination) : destination{std::move(destination)} {}
void Extractor::extract(const std::filesystem::path &archivePath) {
unzFile zipFile = unzOpen(archivePath.c_str());
if (zipFile == nullptr) {
return;
}
unz_global_info globalInfo;
if (unzGetGlobalInfo(zipFile, &globalInfo) != UNZ_OK) {
unzClose(zipFile);
return;
}
char readBuf[BUFFER_SIZE], filenameBuf[BUFFER_SIZE];
for (uLong i = 0; i < globalInfo.number_entry; ++i) {
unz_file_info fileInfo;
if (unzGetCurrentFileInfo(zipFile, &fileInfo, filenameBuf, BUFFER_SIZE, nullptr, 0, nullptr, 0) != UNZ_OK) {
unzClose(zipFile);
return;
}
std::string_view filename = filenameBuf;
if (filename.back() == '/') {
mkdir((destination / filename).c_str(), DIRECTORY_PERMISSIONS);
} else {
if (unzOpenCurrentFile(zipFile) != UNZ_OK) {
unzClose(zipFile);
return;
}
FILE *out = fopen((destination / filename).c_str(), "wb");
if (out == nullptr) {
unzCloseCurrentFile(zipFile);
unzClose(zipFile);
return;
}
int error = UNZ_OK;
do {
error = unzReadCurrentFile(zipFile, readBuf, BUFFER_SIZE);
if (error < 0) {
unzCloseCurrentFile(zipFile);
unzClose(zipFile);
return;
}
if (error > 0) {
fwrite(readBuf, error, 1, out);
}
} while (error > 0);
fclose(out);
}
unzCloseCurrentFile(zipFile);
if ((i + 1) < globalInfo.number_entry) {
if (unzGoToNextFile(zipFile) != UNZ_OK) {
unzClose(zipFile);
return;
}
}
}
unzClose(zipFile);
}
}

13
cli/src/io/Archive.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <filesystem>
namespace oki{
class Extractor {
private:
std::filesystem::path destination;
public:
explicit Extractor(std::filesystem::path destination);
void extract(const std::filesystem::path &archivePath);
};
}

View File

@@ -56,9 +56,9 @@ namespace oki{
return curl_easy_strerror(static_cast<CURLcode>(code));
}
APIExeption::APIExeption(std::string_view msg) : msg{msg} {}
APIException::APIException(std::string_view msg) : msg{msg} {}
const char *APIExeption::what() const noexcept {
const char *APIException::what() const noexcept {
return this->msg.c_str();
}
}

View File

@@ -23,11 +23,11 @@ namespace oki{
const char *what() const noexcept override;
};
class APIExeption : public std::exception {
class APIException : public std::exception {
private:
std::string msg;
public:
explicit APIExeption(std::string_view msg);
explicit APIException(std::string_view msg);
const char *what() const noexcept override;
};
}

16
cli/src/io/TmpFile.cpp Normal file
View File

@@ -0,0 +1,16 @@
#include "TmpFile.h"
#include <cstdlib>
#include <unistd.h>
namespace oki{
TmpFile::TmpFile() : filename{"/tmp/oki-XXXXXX"}, fd{mkstemp(this->filename)} {}
const char *TmpFile::getFilename() {
return filename;
}
TmpFile::~TmpFile() {
unlink(filename);
}
}

13
cli/src/io/TmpFile.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
namespace oki{
class TmpFile {
private:
char filename[20];
int fd;
public:
TmpFile();
const char *getFilename();
~TmpFile();
};
}

View File

@@ -16,14 +16,19 @@ int main(int argc, char *argv[]) {
std::cout << package.getShortName() << ": " << package.getLongName() << "\n";
}
} else if (auto* install = std::get_if<InstallAction>(&action)) {
std::cout << "Installing " << install->packageName << "...\n";
std::optional<Package> p = repository.showPackage(install->packageName);
if (p == std::nullopt || p->getVersions().empty()) {
std::cerr << "This packet doesn't exist or doesn't have any version\n";
} else {
repository.download(p->getVersions().front(), "dependencies");
}
} else if (auto* show = std::get_if<ShowAction>(&action)){
std::optional<Package> p = repository.showPackage(show->packageName);
if (p == std::nullopt) {
std::cout << "Le package n'existe pas\n";
std::cerr << "This packet doesn't exist\n";
} else {
if (color) {
std::cout << "\e[32m" << p->getShortName() << "\e[0m";
std::cout << "\x1B[32m" << p->getShortName() << "\x1B[0m";
} else {
std::cout << p->getShortName();
}

View File

@@ -1,7 +1,8 @@
#include "Version.h"
namespace oki{
Version::Version(std::string_view identifier, std::string_view publishedDate): identifier{identifier}, publishedDate{publishedDate}{}
Version::Version(std::string_view identifier, std::string_view publishedDate, std::string_view downloadUrl)
: identifier{identifier}, publishedDate{publishedDate}, downloadUrl{downloadUrl} {}
const std::string& Version::getIdentifier() const{
return this->identifier;
@@ -9,4 +10,7 @@ namespace oki{
const std::string& Version::getPublishedDate() const{
return this->publishedDate;
}
const std::string& Version::getDownloadUrl() const{
return this->downloadUrl;
}
}

View File

@@ -8,9 +8,11 @@ namespace oki{
private:
std::string identifier;
std::string publishedDate;
std::string downloadUrl;
public:
Version(std::string_view identifier, std::string_view publishedDate);
Version(std::string_view identifier, std::string_view publishedDate, std::string_view downloadUrl);
const std::string& getIdentifier() const;
const std::string& getPublishedDate() const;
const std::string& getDownloadUrl() const;
};
}

View File

@@ -19,8 +19,8 @@ namespace oki{
return packages;
}
void LocalRepository::download(std::string_view packageName, const fs::path& destination) {
std::cerr << "TODO : downloading " << packageName << "\n";
void LocalRepository::download(const Version &packageVersion, const fs::path& destination) {
std::cerr << "TODO : downloading " << packageVersion.getIdentifier() << "\n";
}
std::optional<Package> LocalRepository::showPackage(std::string_view packageName){

View File

@@ -11,6 +11,6 @@ namespace oki{
void createIfNotExists();
std::vector<Package> listPackages() override;
std::optional<Package> showPackage(std::string_view packageName) override;
void download(std::string_view packageName, const std::filesystem::path& destination) override;
void download(const Version &packageVersion, const std::filesystem::path& destination) override;
};
}

View File

@@ -1,7 +1,9 @@
#include <nlohmann/json.hpp>
#include "RemoteRepository.h"
#include "../io/Archive.h"
#include "../io/HttpRequest.h"
#include "../io/TmpFile.h"
using json = nlohmann::json;
@@ -18,8 +20,12 @@ namespace oki{
return packages;
}
void RemoteRepository::download(std::string_view packageName, const std::filesystem::path &destination) {
void RemoteRepository::download(const Version &packageVersion, const std::filesystem::path &destination) {
HttpRequest request{apiUrl + packageVersion.getDownloadUrl()};
TmpFile tmp;
request.download(tmp.getFilename());
Extractor extractor{destination};
extractor.extract(tmp.getFilename());
}
std::optional<Package> RemoteRepository::showPackage(std::string_view packageName){
@@ -27,11 +33,11 @@ namespace oki{
json data = json::parse(request.get());
std::vector<Version> versions;
if(data.contains("error")){
throw APIExeption(data.at("error").get<std::string>());
throw APIException(data.at("error").get<std::string>());
}
if(data.contains("versions")){
for (const auto &item : data.at("versions")){
versions.emplace_back(item.at("identifier").get<std::string>(), item.at("published_date").get<std::string>());
versions.emplace_back(item.at("identifier").get<std::string>(), item.at("published_date").get<std::string>(), item.at("download_url").get<std::string>());
}
}
return Package(data.at("short_name").get<std::string>(),data.at("long_name").get<std::string>(), versions);

View File

@@ -10,6 +10,6 @@ namespace oki{
explicit RemoteRepository(std::string_view apiUrl);
std::vector<Package> listPackages() override;
std::optional<Package> showPackage(std::string_view packageName) override;
void download(std::string_view packageName, const std::filesystem::path& destination) override;
void download(const Version &packageVersion, const std::filesystem::path& destination) override;
};
}

View File

@@ -12,7 +12,7 @@ namespace oki{
public:
virtual std::vector<Package> listPackages() = 0;
virtual std::optional<Package> showPackage(std::string_view packageName) = 0;
virtual void download(std::string_view packageName, const std::filesystem::path& destination) = 0;
virtual void download(const Version &packageVersion, const std::filesystem::path& destination) = 0;
virtual ~Repository() = default;
};
}

10
create_packets.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
temp=$(mktemp -d)
mkdir "$temp"/mths
echo '#define MAX(x, y) ((x > y) ? x : y)
#define MIN(x, y) ((x < y) ? x : y)' > "$temp"/mths/mths.c
dest=$(dirname $(realpath "$0"))
echo "$dest"/web/public/packages/mths_0.1.zip
cd "$temp"/mths && zip -r "$dest"/web/public/packages/mths_0.1.zip ./*
rm -r "$temp"

View File

@@ -8,7 +8,8 @@ sqlite3 ../web/oki_packages.db << EOF
INSERT INTO language VALUES (1, 'C');
INSERT INTO package VALUES (1, 'linked-list', 'My amazing linked list package', 1);
INSERT INTO version VALUES (1,1,'1.0.0-ALPHA',CURRENT_TIMESTAMP);
INSERT INTO package VALUES (2, 'linked-list2', 'My second amazing linked list package', 1);
INSERT INTO package VALUES (2, 'mths', 'Min and max library', 1);
INSERT INTO version VALUES (2, 2, '0.1', CURRENT_TIMESTAMP);
EOF
if [ ! -f ../web/public/packages/linked-list_1.0.0-ALPHA.zip ] ; then