1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "devcontainer.h"
#include "devcontainertr.h"
#include <utils/qtcprocess.h>
#include <QCryptographicHash>
using namespace Utils;
namespace DevContainer {
struct InstancePrivate
{
Config config;
Tasking::TaskTree taskTree;
};
Instance::Instance(Config config)
: d(std::make_unique<InstancePrivate>())
{
d->config = std::move(config);
}
Result<std::unique_ptr<Instance>> Instance::fromFile(const FilePath &filePath)
{
const Result<QByteArray> contents = filePath.fileContents();
if (!contents)
return ResultError(contents.error());
const Result<Config> config = Config::fromJson(*contents);
if (!config)
return ResultError(config.error());
return std::make_unique<Instance>(*config);
}
std::unique_ptr<Instance> Instance::fromConfig(const Config &config)
{
return std::make_unique<Instance>(config);
}
Instance::~Instance() {};
using namespace Tasking;
static void connectProcessToLog(
Process &process, const InstanceConfig &instanceConfig, const QString &context)
{
process.setTextChannelMode(Channel::Output, TextChannelMode::SingleLine);
process.setTextChannelMode(Channel::Error, TextChannelMode::SingleLine);
QObject::connect(
&process, &Process::textOnStandardOutput, [instanceConfig, context](const QString &text) {
instanceConfig.logFunction(QString("[%1] %2").arg(context).arg(text.trimmed()));
});
QObject::connect(
&process, &Process::textOnStandardError, [instanceConfig, context](const QString &text) {
instanceConfig.logFunction(QString("[%1] %2").arg(context).arg(text.trimmed()));
});
}
static Group prepareContainerRecipe(
const DockerfileContainer &containerConfig, const InstanceConfig &instanceConfig)
{
return Group{ProcessTask([containerConfig, instanceConfig](Process &process) {
connectProcessToLog(process, instanceConfig, Tr::tr("Build Dockerfile"));
const FilePath configFileDir = instanceConfig.configFilePath.parentDir();
const FilePath contextPath = configFileDir.resolvePath(
containerConfig.context.value_or("."));
const FilePath dockerFile = configFileDir.resolvePath(containerConfig.dockerfile);
const QString hash = QString::fromLatin1(
QCryptographicHash::hash(
instanceConfig.workspaceFolder.nativePath().toUtf8(), QCryptographicHash::Sha256)
.toHex());
const QString imageName = QString("qtc-devcontainer-%1").arg(hash);
CommandLine buildCmdLine{
instanceConfig.dockerCli,
{"build", "-f", dockerFile.nativePath(), "-t", imageName, contextPath.nativePath()}};
process.setCommand(buildCmdLine);
process.setWorkingDirectory(instanceConfig.workspaceFolder);
instanceConfig.logFunction(
QString(Tr::tr("Building Dockerfile: %1")).arg(process.commandLine().toUserOutput()));
})};
}
static Group prepareContainerRecipe(
const ImageContainer &config, const InstanceConfig &instanceConfig)
{
Q_UNUSED(config);
Q_UNUSED(instanceConfig);
return Group{};
}
static Group prepareContainerRecipe(
const ComposeContainer &config, const InstanceConfig &instanceConfig)
{
Q_UNUSED(config);
Q_UNUSED(instanceConfig);
return Group{};
}
static Group prepareContainerRecipe(const std::monostate &, const InstanceConfig &instanceConfig)
{
Q_UNUSED(instanceConfig);
return {};
}
static Group prepareRecipe(const Config &config, const InstanceConfig &instanceConfig)
{
return std::visit(
[&instanceConfig](const auto &containerConfig) {
return prepareContainerRecipe(containerConfig, instanceConfig);
},
config.containerConfig);
}
void Instance::up(const InstanceConfig &instanceConfig)
{
d->taskTree.setRecipe(upRecipe(instanceConfig));
d->taskTree.start();
}
void Instance::down() {}
Tasking::Group Instance::upRecipe(const InstanceConfig &instanceConfig) const
{
return prepareRecipe(d->config, instanceConfig);
}
Tasking::Group Instance::downRecipe() const
{
return Group{};
}
} // namespace DevContainer
|