cli: Télécharge et extrait les paquets d'un .zip
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,3 +14,4 @@ oki
|
||||
|
||||
# Dependencies
|
||||
third-party
|
||||
dependencies
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
76
cli/src/io/Archive.cpp
Normal 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
13
cli/src/io/Archive.h
Normal 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);
|
||||
};
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
16
cli/src/io/TmpFile.cpp
Normal 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
13
cli/src/io/TmpFile.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace oki{
|
||||
class TmpFile {
|
||||
private:
|
||||
char filename[20];
|
||||
int fd;
|
||||
public:
|
||||
TmpFile();
|
||||
const char *getFilename();
|
||||
~TmpFile();
|
||||
};
|
||||
}
|
@@ -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();
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
};
|
||||
}
|
@@ -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){
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
@@ -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
10
create_packets.sh
Executable 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"
|
@@ -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
|
||||
|
Reference in New Issue
Block a user