openship 0.2.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -61
- package/dist/bare-ITM45QBX.js +18 -0
- package/dist/bowser-MFQA6OS4.js +2821 -0
- package/dist/chunk-3U4SPZD5.js +241 -0
- package/dist/chunk-46YJFP4S.js +1771 -0
- package/dist/chunk-4CQWT2WL.js +116 -0
- package/dist/chunk-4FABQGQP.js +23 -0
- package/dist/chunk-4LEL3H2Z.js +84 -0
- package/dist/chunk-4MG3BVHE.js +326 -0
- package/dist/chunk-4VNS5WPM.js +42 -0
- package/dist/chunk-5N7VUGL6.js +2094 -0
- package/dist/chunk-5ZCKQ2MI.js +1360 -0
- package/dist/chunk-6ERIMTBC.js +1412 -0
- package/dist/chunk-7UOB5GGQ.js +132 -0
- package/dist/chunk-BUBSSKO6.js +1384 -0
- package/dist/chunk-E6W2JMMU.js +341 -0
- package/dist/chunk-E77LJWJK.js +1535 -0
- package/dist/chunk-F4DFVG6Q.js +573 -0
- package/dist/chunk-HFYDYK4D.js +1092 -0
- package/dist/chunk-JR2OCMJA.js +46 -0
- package/dist/chunk-JUJBBF5U.js +746 -0
- package/dist/chunk-JZLNHGLW.js +19145 -0
- package/dist/chunk-KD7VO4LG.js +2617 -0
- package/dist/chunk-LBFH7THC.js +435 -0
- package/dist/chunk-NB2VEIWG.js +129 -0
- package/dist/chunk-OJE6YWPQ.js +2866 -0
- package/dist/chunk-QKFRZNKN.js +4941 -0
- package/dist/chunk-RBZPFTHV.js +13354 -0
- package/dist/chunk-S6DSRLNE.js +121 -0
- package/dist/chunk-TEYJYSMT.js +409 -0
- package/dist/chunk-U2DPZSXA.js +112 -0
- package/dist/chunk-WJPCBU7Z.js +3147 -0
- package/dist/chunk-XIFRMKYT.js +13 -0
- package/dist/chunk-XVSAUO2I.js +7946 -0
- package/dist/chunk-Y37KHU7L.js +31 -0
- package/dist/chunk-YL67EWYZ.js +416 -0
- package/dist/chunk-ZKJOFWBJ.js +1879 -0
- package/dist/cloud-JMPHO3CW.js +7 -0
- package/dist/cloud-W5G6YXAY.js +24 -0
- package/dist/dist-L3MPOWCA.js +65 -0
- package/dist/dist-es-4C322O5D.js +22 -0
- package/dist/dist-es-GTAMF674.js +87 -0
- package/dist/dist-es-KE56MWYO.js +169 -0
- package/dist/dist-es-NRZIQY5B.js +484 -0
- package/dist/dist-es-WZCR7DHQ.js +317 -0
- package/dist/dist-es-XQCOTGC7.js +376 -0
- package/dist/dist-es-ZW3HFQUM.js +69 -0
- package/dist/docker-3FVJMFC3.js +12 -0
- package/dist/docker-edge-executor-ZIWAZWPL.js +9 -0
- package/dist/edge-import-53M5DGWZ.js +39 -0
- package/dist/event-streams-7LGDL56D.js +42 -0
- package/dist/executor-J7BHW5HF.js +26 -0
- package/dist/index.js +2459 -3461
- package/dist/loadSso-N273C5I7.js +577 -0
- package/dist/nginx-YZWKYKQU.js +16 -0
- package/dist/noop-RRNCIUI5.js +7 -0
- package/dist/openresty-lua-HAO4EUDY.js +32 -0
- package/dist/server/index.js +217916 -205055
- package/dist/server/migrations/0057_complete_prism.sql +18 -0
- package/dist/server/migrations/0058_add_server_is_local.sql +1 -0
- package/dist/server/migrations/0059_legal_fixer.sql +18 -0
- package/dist/server/migrations/0060_add_route_strategy_host_port.sql +3 -0
- package/dist/server/migrations/0061_notification_default_multi_kind.sql +3 -0
- package/dist/server/migrations/0062_add_incoming_webhook.sql +21 -0
- package/dist/server/migrations/0063_add_migration_logs.sql +1 -0
- package/dist/server/migrations/0064_add_migration_partial.sql +2 -0
- package/dist/server/migrations/0065_custom_app_template.sql +14 -0
- package/dist/server/migrations/0066_ensure_migration_partial_columns.sql +2 -0
- package/dist/server/migrations/0067_webhook_delivery.sql +28 -0
- package/dist/server/migrations/0068_add_migration_target_volumes.sql +1 -0
- package/dist/server/migrations/0069_add_project_composite_routes.sql +1 -0
- package/dist/server/migrations/0070_forward_git_to_server.sql +1 -0
- package/dist/server/migrations/meta/0057_snapshot.json +9326 -0
- package/dist/server/migrations/meta/0058_snapshot.json +9333 -0
- package/dist/server/migrations/meta/0059_snapshot.json +9495 -0
- package/dist/server/migrations/meta/0060_snapshot.json +9515 -0
- package/dist/server/migrations/meta/_journal.json +99 -1
- package/dist/setup-5X3WEEHA.js +14 -0
- package/dist/signin-DR462ERO.js +663 -0
- package/dist/sso-oidc-VBZLJUK5.js +830 -0
- package/dist/sts-EJLZ4Z77.js +1422 -0
- package/package.json +6 -2
|
@@ -0,0 +1,1092 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assembleGitClone,
|
|
3
|
+
localGitSshWriter,
|
|
4
|
+
materializeGitSsh,
|
|
5
|
+
sq
|
|
6
|
+
} from "./chunk-YL67EWYZ.js";
|
|
7
|
+
import {
|
|
8
|
+
getTarCreateEnv,
|
|
9
|
+
prepareSourceTarArgs
|
|
10
|
+
} from "./chunk-4MG3BVHE.js";
|
|
11
|
+
import {
|
|
12
|
+
packageManagerEnsureCommand
|
|
13
|
+
} from "./chunk-JZLNHGLW.js";
|
|
14
|
+
import {
|
|
15
|
+
__commonJS,
|
|
16
|
+
__toESM
|
|
17
|
+
} from "./chunk-4VNS5WPM.js";
|
|
18
|
+
|
|
19
|
+
// ../../node_modules/ignore/index.js
|
|
20
|
+
var require_ignore = __commonJS({
|
|
21
|
+
"../../node_modules/ignore/index.js"(exports, module) {
|
|
22
|
+
"use strict";
|
|
23
|
+
function makeArray(subject) {
|
|
24
|
+
return Array.isArray(subject) ? subject : [subject];
|
|
25
|
+
}
|
|
26
|
+
var UNDEFINED = void 0;
|
|
27
|
+
var EMPTY = "";
|
|
28
|
+
var SPACE = " ";
|
|
29
|
+
var ESCAPE = "\\";
|
|
30
|
+
var REGEX_TEST_BLANK_LINE = /^\s+$/;
|
|
31
|
+
var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
|
|
32
|
+
var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
|
|
33
|
+
var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
|
|
34
|
+
var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
35
|
+
var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
|
|
36
|
+
var REGEX_TEST_TRAILING_SLASH = /\/$/;
|
|
37
|
+
var SLASH = "/";
|
|
38
|
+
var TMP_KEY_IGNORE = "node-ignore";
|
|
39
|
+
if (typeof Symbol !== "undefined") {
|
|
40
|
+
TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
|
|
41
|
+
}
|
|
42
|
+
var KEY_IGNORE = TMP_KEY_IGNORE;
|
|
43
|
+
var define = (object, key, value) => {
|
|
44
|
+
Object.defineProperty(object, key, { value });
|
|
45
|
+
return value;
|
|
46
|
+
};
|
|
47
|
+
var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
|
|
48
|
+
var RETURN_FALSE = () => false;
|
|
49
|
+
var sanitizeRange = (range) => range.replace(
|
|
50
|
+
REGEX_REGEXP_RANGE,
|
|
51
|
+
(match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY
|
|
52
|
+
);
|
|
53
|
+
var cleanRangeBackSlash = (slashes) => {
|
|
54
|
+
const { length } = slashes;
|
|
55
|
+
return slashes.slice(0, length - length % 2);
|
|
56
|
+
};
|
|
57
|
+
var REPLACERS = [
|
|
58
|
+
[
|
|
59
|
+
// Remove BOM
|
|
60
|
+
// TODO:
|
|
61
|
+
// Other similar zero-width characters?
|
|
62
|
+
/^\uFEFF/,
|
|
63
|
+
() => EMPTY
|
|
64
|
+
],
|
|
65
|
+
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
|
66
|
+
[
|
|
67
|
+
// (a\ ) -> (a )
|
|
68
|
+
// (a ) -> (a)
|
|
69
|
+
// (a ) -> (a)
|
|
70
|
+
// (a \ ) -> (a )
|
|
71
|
+
/((?:\\\\)*?)(\\?\s+)$/,
|
|
72
|
+
(_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
|
|
73
|
+
],
|
|
74
|
+
// Replace (\ ) with ' '
|
|
75
|
+
// (\ ) -> ' '
|
|
76
|
+
// (\\ ) -> '\\ '
|
|
77
|
+
// (\\\ ) -> '\\ '
|
|
78
|
+
[
|
|
79
|
+
/(\\+?)\s/g,
|
|
80
|
+
(_, m1) => {
|
|
81
|
+
const { length } = m1;
|
|
82
|
+
return m1.slice(0, length - length % 2) + SPACE;
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
// Escape metacharacters
|
|
86
|
+
// which is written down by users but means special for regular expressions.
|
|
87
|
+
// > There are 12 characters with special meanings:
|
|
88
|
+
// > - the backslash \,
|
|
89
|
+
// > - the caret ^,
|
|
90
|
+
// > - the dollar sign $,
|
|
91
|
+
// > - the period or dot .,
|
|
92
|
+
// > - the vertical bar or pipe symbol |,
|
|
93
|
+
// > - the question mark ?,
|
|
94
|
+
// > - the asterisk or star *,
|
|
95
|
+
// > - the plus sign +,
|
|
96
|
+
// > - the opening parenthesis (,
|
|
97
|
+
// > - the closing parenthesis ),
|
|
98
|
+
// > - and the opening square bracket [,
|
|
99
|
+
// > - the opening curly brace {,
|
|
100
|
+
// > These special characters are often called "metacharacters".
|
|
101
|
+
[
|
|
102
|
+
/[\\$.|*+(){^]/g,
|
|
103
|
+
(match) => `\\${match}`
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
// > a question mark (?) matches a single character
|
|
107
|
+
/(?!\\)\?/g,
|
|
108
|
+
() => "[^/]"
|
|
109
|
+
],
|
|
110
|
+
// leading slash
|
|
111
|
+
[
|
|
112
|
+
// > A leading slash matches the beginning of the pathname.
|
|
113
|
+
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
|
114
|
+
// A leading slash matches the beginning of the pathname
|
|
115
|
+
/^\//,
|
|
116
|
+
() => "^"
|
|
117
|
+
],
|
|
118
|
+
// replace special metacharacter slash after the leading slash
|
|
119
|
+
[
|
|
120
|
+
/\//g,
|
|
121
|
+
() => "\\/"
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
// > A leading "**" followed by a slash means match in all directories.
|
|
125
|
+
// > For example, "**/foo" matches file or directory "foo" anywhere,
|
|
126
|
+
// > the same as pattern "foo".
|
|
127
|
+
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly
|
|
128
|
+
// > under directory "foo".
|
|
129
|
+
// Notice that the '*'s have been replaced as '\\*'
|
|
130
|
+
/^\^*\\\*\\\*\\\//,
|
|
131
|
+
// '**/foo' <-> 'foo'
|
|
132
|
+
() => "^(?:.*\\/)?"
|
|
133
|
+
],
|
|
134
|
+
// starting
|
|
135
|
+
[
|
|
136
|
+
// there will be no leading '/'
|
|
137
|
+
// (which has been replaced by section "leading slash")
|
|
138
|
+
// If starts with '**', adding a '^' to the regular expression also works
|
|
139
|
+
/^(?=[^^])/,
|
|
140
|
+
function startingReplacer() {
|
|
141
|
+
return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
// two globstars
|
|
145
|
+
[
|
|
146
|
+
// Use lookahead assertions so that we could match more than one `'/**'`
|
|
147
|
+
/\\\/\\\*\\\*(?=\\\/|$)/g,
|
|
148
|
+
// Zero, one or several directories
|
|
149
|
+
// should not use '*', or it will be replaced by the next replacer
|
|
150
|
+
// Check if it is not the last `'/**'`
|
|
151
|
+
(_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
|
|
152
|
+
],
|
|
153
|
+
// normal intermediate wildcards
|
|
154
|
+
[
|
|
155
|
+
// Never replace escaped '*'
|
|
156
|
+
// ignore rule '\*' will match the path '*'
|
|
157
|
+
// 'abc.*/' -> go
|
|
158
|
+
// 'abc.*' -> skip this rule,
|
|
159
|
+
// coz trailing single wildcard will be handed by [trailing wildcard]
|
|
160
|
+
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
|
161
|
+
// '*.js' matches '.js'
|
|
162
|
+
// '*.js' doesn't match 'abc'
|
|
163
|
+
(_, p1, p2) => {
|
|
164
|
+
const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
|
|
165
|
+
return p1 + unescaped;
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
[
|
|
169
|
+
// unescape, revert step 3 except for back slash
|
|
170
|
+
// For example, if a user escape a '\\*',
|
|
171
|
+
// after step 3, the result will be '\\\\\\*'
|
|
172
|
+
/\\\\\\(?=[$.|*+(){^])/g,
|
|
173
|
+
() => ESCAPE
|
|
174
|
+
],
|
|
175
|
+
[
|
|
176
|
+
// '\\\\' -> '\\'
|
|
177
|
+
/\\\\/g,
|
|
178
|
+
() => ESCAPE
|
|
179
|
+
],
|
|
180
|
+
[
|
|
181
|
+
// > The range notation, e.g. [a-zA-Z],
|
|
182
|
+
// > can be used to match one of the characters in a range.
|
|
183
|
+
// `\` is escaped by step 3
|
|
184
|
+
/(\\)?\[([^\]/]*?)(\\*)($|\])/g,
|
|
185
|
+
(match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range)}${endEscape}]` : "[]" : "[]"
|
|
186
|
+
],
|
|
187
|
+
// ending
|
|
188
|
+
[
|
|
189
|
+
// 'js' will not match 'js.'
|
|
190
|
+
// 'ab' will not match 'abc'
|
|
191
|
+
/(?:[^*])$/,
|
|
192
|
+
// WTF!
|
|
193
|
+
// https://git-scm.com/docs/gitignore
|
|
194
|
+
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
|
195
|
+
// which re-fixes #24, #38
|
|
196
|
+
// > If there is a separator at the end of the pattern then the pattern
|
|
197
|
+
// > will only match directories, otherwise the pattern can match both
|
|
198
|
+
// > files and directories.
|
|
199
|
+
// 'js*' will not match 'a.js'
|
|
200
|
+
// 'js/' will not match 'a.js'
|
|
201
|
+
// 'js' will match 'a.js' and 'a.js/'
|
|
202
|
+
(match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)`
|
|
203
|
+
]
|
|
204
|
+
];
|
|
205
|
+
var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
|
|
206
|
+
var MODE_IGNORE = "regex";
|
|
207
|
+
var MODE_CHECK_IGNORE = "checkRegex";
|
|
208
|
+
var UNDERSCORE = "_";
|
|
209
|
+
var TRAILING_WILD_CARD_REPLACERS = {
|
|
210
|
+
[MODE_IGNORE](_, p1) {
|
|
211
|
+
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
212
|
+
return `${prefix}(?=$|\\/$)`;
|
|
213
|
+
},
|
|
214
|
+
[MODE_CHECK_IGNORE](_, p1) {
|
|
215
|
+
const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
|
|
216
|
+
return `${prefix}(?=$|\\/$)`;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
var makeRegexPrefix = (pattern) => REPLACERS.reduce(
|
|
220
|
+
(prev, [matcher, replacer]) => prev.replace(matcher, replacer.bind(pattern)),
|
|
221
|
+
pattern
|
|
222
|
+
);
|
|
223
|
+
var isString = (subject) => typeof subject === "string";
|
|
224
|
+
var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
|
|
225
|
+
var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
|
|
226
|
+
var IgnoreRule = class {
|
|
227
|
+
constructor(pattern, mark, body, ignoreCase, negative, prefix) {
|
|
228
|
+
this.pattern = pattern;
|
|
229
|
+
this.mark = mark;
|
|
230
|
+
this.negative = negative;
|
|
231
|
+
define(this, "body", body);
|
|
232
|
+
define(this, "ignoreCase", ignoreCase);
|
|
233
|
+
define(this, "regexPrefix", prefix);
|
|
234
|
+
}
|
|
235
|
+
get regex() {
|
|
236
|
+
const key = UNDERSCORE + MODE_IGNORE;
|
|
237
|
+
if (this[key]) {
|
|
238
|
+
return this[key];
|
|
239
|
+
}
|
|
240
|
+
return this._make(MODE_IGNORE, key);
|
|
241
|
+
}
|
|
242
|
+
get checkRegex() {
|
|
243
|
+
const key = UNDERSCORE + MODE_CHECK_IGNORE;
|
|
244
|
+
if (this[key]) {
|
|
245
|
+
return this[key];
|
|
246
|
+
}
|
|
247
|
+
return this._make(MODE_CHECK_IGNORE, key);
|
|
248
|
+
}
|
|
249
|
+
_make(mode, key) {
|
|
250
|
+
const str = this.regexPrefix.replace(
|
|
251
|
+
REGEX_REPLACE_TRAILING_WILDCARD,
|
|
252
|
+
// It does not need to bind pattern
|
|
253
|
+
TRAILING_WILD_CARD_REPLACERS[mode]
|
|
254
|
+
);
|
|
255
|
+
const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
|
|
256
|
+
return define(this, key, regex);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
var createRule = ({
|
|
260
|
+
pattern,
|
|
261
|
+
mark
|
|
262
|
+
}, ignoreCase) => {
|
|
263
|
+
let negative = false;
|
|
264
|
+
let body = pattern;
|
|
265
|
+
if (body.indexOf("!") === 0) {
|
|
266
|
+
negative = true;
|
|
267
|
+
body = body.substr(1);
|
|
268
|
+
}
|
|
269
|
+
body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
|
|
270
|
+
const regexPrefix = makeRegexPrefix(body);
|
|
271
|
+
return new IgnoreRule(
|
|
272
|
+
pattern,
|
|
273
|
+
mark,
|
|
274
|
+
body,
|
|
275
|
+
ignoreCase,
|
|
276
|
+
negative,
|
|
277
|
+
regexPrefix
|
|
278
|
+
);
|
|
279
|
+
};
|
|
280
|
+
var RuleManager = class {
|
|
281
|
+
constructor(ignoreCase) {
|
|
282
|
+
this._ignoreCase = ignoreCase;
|
|
283
|
+
this._rules = [];
|
|
284
|
+
}
|
|
285
|
+
_add(pattern) {
|
|
286
|
+
if (pattern && pattern[KEY_IGNORE]) {
|
|
287
|
+
this._rules = this._rules.concat(pattern._rules._rules);
|
|
288
|
+
this._added = true;
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (isString(pattern)) {
|
|
292
|
+
pattern = {
|
|
293
|
+
pattern
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
if (checkPattern(pattern.pattern)) {
|
|
297
|
+
const rule = createRule(pattern, this._ignoreCase);
|
|
298
|
+
this._added = true;
|
|
299
|
+
this._rules.push(rule);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// @param {Array<string> | string | Ignore} pattern
|
|
303
|
+
add(pattern) {
|
|
304
|
+
this._added = false;
|
|
305
|
+
makeArray(
|
|
306
|
+
isString(pattern) ? splitPattern(pattern) : pattern
|
|
307
|
+
).forEach(this._add, this);
|
|
308
|
+
return this._added;
|
|
309
|
+
}
|
|
310
|
+
// Test one single path without recursively checking parent directories
|
|
311
|
+
//
|
|
312
|
+
// - checkUnignored `boolean` whether should check if the path is unignored,
|
|
313
|
+
// setting `checkUnignored` to `false` could reduce additional
|
|
314
|
+
// path matching.
|
|
315
|
+
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
316
|
+
// @returns {TestResult} true if a file is ignored
|
|
317
|
+
test(path, checkUnignored, mode) {
|
|
318
|
+
let ignored = false;
|
|
319
|
+
let unignored = false;
|
|
320
|
+
let matchedRule;
|
|
321
|
+
this._rules.forEach((rule) => {
|
|
322
|
+
const { negative } = rule;
|
|
323
|
+
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const matched = rule[mode].test(path);
|
|
327
|
+
if (!matched) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
ignored = !negative;
|
|
331
|
+
unignored = negative;
|
|
332
|
+
matchedRule = negative ? UNDEFINED : rule;
|
|
333
|
+
});
|
|
334
|
+
const ret = {
|
|
335
|
+
ignored,
|
|
336
|
+
unignored
|
|
337
|
+
};
|
|
338
|
+
if (matchedRule) {
|
|
339
|
+
ret.rule = matchedRule;
|
|
340
|
+
}
|
|
341
|
+
return ret;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
var throwError = (message, Ctor) => {
|
|
345
|
+
throw new Ctor(message);
|
|
346
|
+
};
|
|
347
|
+
var checkPath = (path, originalPath, doThrow) => {
|
|
348
|
+
if (!isString(path)) {
|
|
349
|
+
return doThrow(
|
|
350
|
+
`path must be a string, but got \`${originalPath}\``,
|
|
351
|
+
TypeError
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
if (!path) {
|
|
355
|
+
return doThrow(`path must not be empty`, TypeError);
|
|
356
|
+
}
|
|
357
|
+
if (checkPath.isNotRelative(path)) {
|
|
358
|
+
const r = "`path.relative()`d";
|
|
359
|
+
return doThrow(
|
|
360
|
+
`path should be a ${r} string, but got "${originalPath}"`,
|
|
361
|
+
RangeError
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
};
|
|
366
|
+
var isNotRelative = (path) => REGEX_TEST_INVALID_PATH.test(path);
|
|
367
|
+
checkPath.isNotRelative = isNotRelative;
|
|
368
|
+
checkPath.convert = (p) => p;
|
|
369
|
+
var Ignore = class {
|
|
370
|
+
constructor({
|
|
371
|
+
ignorecase = true,
|
|
372
|
+
ignoreCase = ignorecase,
|
|
373
|
+
allowRelativePaths = false
|
|
374
|
+
} = {}) {
|
|
375
|
+
define(this, KEY_IGNORE, true);
|
|
376
|
+
this._rules = new RuleManager(ignoreCase);
|
|
377
|
+
this._strictPathCheck = !allowRelativePaths;
|
|
378
|
+
this._initCache();
|
|
379
|
+
}
|
|
380
|
+
_initCache() {
|
|
381
|
+
this._ignoreCache = /* @__PURE__ */ Object.create(null);
|
|
382
|
+
this._testCache = /* @__PURE__ */ Object.create(null);
|
|
383
|
+
}
|
|
384
|
+
add(pattern) {
|
|
385
|
+
if (this._rules.add(pattern)) {
|
|
386
|
+
this._initCache();
|
|
387
|
+
}
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
390
|
+
// legacy
|
|
391
|
+
addPattern(pattern) {
|
|
392
|
+
return this.add(pattern);
|
|
393
|
+
}
|
|
394
|
+
// @returns {TestResult}
|
|
395
|
+
_test(originalPath, cache, checkUnignored, slices) {
|
|
396
|
+
const path = originalPath && checkPath.convert(originalPath);
|
|
397
|
+
checkPath(
|
|
398
|
+
path,
|
|
399
|
+
originalPath,
|
|
400
|
+
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
401
|
+
);
|
|
402
|
+
return this._t(path, cache, checkUnignored, slices);
|
|
403
|
+
}
|
|
404
|
+
checkIgnore(path) {
|
|
405
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
|
|
406
|
+
return this.test(path);
|
|
407
|
+
}
|
|
408
|
+
const slices = path.split(SLASH).filter(Boolean);
|
|
409
|
+
slices.pop();
|
|
410
|
+
if (slices.length) {
|
|
411
|
+
const parent = this._t(
|
|
412
|
+
slices.join(SLASH) + SLASH,
|
|
413
|
+
this._testCache,
|
|
414
|
+
true,
|
|
415
|
+
slices
|
|
416
|
+
);
|
|
417
|
+
if (parent.ignored) {
|
|
418
|
+
return parent;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return this._rules.test(path, false, MODE_CHECK_IGNORE);
|
|
422
|
+
}
|
|
423
|
+
_t(path, cache, checkUnignored, slices) {
|
|
424
|
+
if (path in cache) {
|
|
425
|
+
return cache[path];
|
|
426
|
+
}
|
|
427
|
+
if (!slices) {
|
|
428
|
+
slices = path.split(SLASH).filter(Boolean);
|
|
429
|
+
}
|
|
430
|
+
slices.pop();
|
|
431
|
+
if (!slices.length) {
|
|
432
|
+
return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE);
|
|
433
|
+
}
|
|
434
|
+
const parent = this._t(
|
|
435
|
+
slices.join(SLASH) + SLASH,
|
|
436
|
+
cache,
|
|
437
|
+
checkUnignored,
|
|
438
|
+
slices
|
|
439
|
+
);
|
|
440
|
+
return cache[path] = parent.ignored ? parent : this._rules.test(path, checkUnignored, MODE_IGNORE);
|
|
441
|
+
}
|
|
442
|
+
ignores(path) {
|
|
443
|
+
return this._test(path, this._ignoreCache, false).ignored;
|
|
444
|
+
}
|
|
445
|
+
createFilter() {
|
|
446
|
+
return (path) => !this.ignores(path);
|
|
447
|
+
}
|
|
448
|
+
filter(paths) {
|
|
449
|
+
return makeArray(paths).filter(this.createFilter());
|
|
450
|
+
}
|
|
451
|
+
// @returns {TestResult}
|
|
452
|
+
test(path) {
|
|
453
|
+
return this._test(path, this._testCache, true);
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
var factory = (options) => new Ignore(options);
|
|
457
|
+
var isPathValid = (path) => checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
|
|
458
|
+
var setupWindows = () => {
|
|
459
|
+
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
460
|
+
checkPath.convert = makePosix;
|
|
461
|
+
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
462
|
+
checkPath.isNotRelative = (path) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path) || isNotRelative(path);
|
|
463
|
+
};
|
|
464
|
+
if (
|
|
465
|
+
// Detect `process` so that it can run in browsers.
|
|
466
|
+
typeof process !== "undefined" && process.platform === "win32"
|
|
467
|
+
) {
|
|
468
|
+
setupWindows();
|
|
469
|
+
}
|
|
470
|
+
module.exports = factory;
|
|
471
|
+
factory.default = factory;
|
|
472
|
+
module.exports.isPathValid = isPathValid;
|
|
473
|
+
define(module.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// ../../packages/adapters/src/runtime/docker-paths.ts
|
|
478
|
+
import { access, readdir } from "fs/promises";
|
|
479
|
+
import { isAbsolute, join, relative, sep } from "path";
|
|
480
|
+
function toPosixPath(value) {
|
|
481
|
+
return value.split(sep).filter(Boolean).join("/");
|
|
482
|
+
}
|
|
483
|
+
function normalizeDockerRelativePath(value) {
|
|
484
|
+
const normalized = value?.trim().replace(/^\.\//, "").replace(/^\/+|\/+$/g, "");
|
|
485
|
+
if (!normalized || normalized === ".") {
|
|
486
|
+
return "";
|
|
487
|
+
}
|
|
488
|
+
return normalized.split(/[\\/]/).filter(Boolean).join("/");
|
|
489
|
+
}
|
|
490
|
+
function normalizeDockerRootDirectory(rootDirectory, localPath) {
|
|
491
|
+
let normalized = rootDirectory?.trim() ?? "";
|
|
492
|
+
if (!normalized) {
|
|
493
|
+
return "";
|
|
494
|
+
}
|
|
495
|
+
if (localPath && isAbsolute(normalized)) {
|
|
496
|
+
const relativePath = relative(localPath, normalized);
|
|
497
|
+
if (!relativePath || relativePath === ".") {
|
|
498
|
+
return "";
|
|
499
|
+
}
|
|
500
|
+
if (!relativePath.startsWith("..") && !isAbsolute(relativePath)) {
|
|
501
|
+
normalized = relativePath;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
normalized = normalized.replace(/^\.\//, "").replace(/^\/+|\/+$/g, "");
|
|
505
|
+
if (!normalized || normalized === ".") {
|
|
506
|
+
return "";
|
|
507
|
+
}
|
|
508
|
+
return toPosixPath(normalized);
|
|
509
|
+
}
|
|
510
|
+
function resolveExplicitDockerfileCandidate(rootDirectory, dockerfilePath) {
|
|
511
|
+
const normalizedRootDirectory = normalizeDockerRelativePath(rootDirectory);
|
|
512
|
+
const normalizedDockerfilePath = normalizeDockerRelativePath(dockerfilePath);
|
|
513
|
+
if (!normalizedDockerfilePath) {
|
|
514
|
+
return "";
|
|
515
|
+
}
|
|
516
|
+
if (!normalizedRootDirectory) {
|
|
517
|
+
return normalizedDockerfilePath;
|
|
518
|
+
}
|
|
519
|
+
if (normalizedDockerfilePath.startsWith(`${normalizedRootDirectory}/`)) {
|
|
520
|
+
return normalizedDockerfilePath;
|
|
521
|
+
}
|
|
522
|
+
return `${normalizedRootDirectory}/${normalizedDockerfilePath}`;
|
|
523
|
+
}
|
|
524
|
+
function resolveDockerfileCandidates(rootDirectory, explicitDockerfilePath) {
|
|
525
|
+
const normalizedRootDirectory = normalizeDockerRelativePath(rootDirectory);
|
|
526
|
+
return [
|
|
527
|
+
resolveExplicitDockerfileCandidate(rootDirectory, explicitDockerfilePath),
|
|
528
|
+
normalizedRootDirectory ? `${normalizedRootDirectory}/Dockerfile` : "Dockerfile",
|
|
529
|
+
"Dockerfile"
|
|
530
|
+
].filter(
|
|
531
|
+
(candidate, index, values) => Boolean(candidate) && values.indexOf(candidate) === index
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
var ROOT_MANIFESTS = [
|
|
535
|
+
"package.json",
|
|
536
|
+
"pyproject.toml",
|
|
537
|
+
"requirements.txt",
|
|
538
|
+
"Pipfile",
|
|
539
|
+
"go.mod",
|
|
540
|
+
"Cargo.toml",
|
|
541
|
+
"Gemfile",
|
|
542
|
+
"composer.json",
|
|
543
|
+
"pom.xml",
|
|
544
|
+
"build.gradle",
|
|
545
|
+
"build.gradle.kts",
|
|
546
|
+
"mix.exs",
|
|
547
|
+
"Dockerfile"
|
|
548
|
+
];
|
|
549
|
+
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
550
|
+
"node_modules",
|
|
551
|
+
".git",
|
|
552
|
+
".next",
|
|
553
|
+
".nuxt",
|
|
554
|
+
".output",
|
|
555
|
+
".turbo",
|
|
556
|
+
".venv",
|
|
557
|
+
"__pycache__",
|
|
558
|
+
"build",
|
|
559
|
+
"dist",
|
|
560
|
+
"target",
|
|
561
|
+
"vendor"
|
|
562
|
+
]);
|
|
563
|
+
async function pathExists(path) {
|
|
564
|
+
return access(path).then(() => true).catch(() => false);
|
|
565
|
+
}
|
|
566
|
+
async function manifestCount(dir) {
|
|
567
|
+
let count = 0;
|
|
568
|
+
for (const file of ROOT_MANIFESTS) {
|
|
569
|
+
if (await pathExists(join(dir, file))) {
|
|
570
|
+
count += 1;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return count;
|
|
574
|
+
}
|
|
575
|
+
function pathSegments(path) {
|
|
576
|
+
return path.toLowerCase().split("/").filter(Boolean);
|
|
577
|
+
}
|
|
578
|
+
function scorePathHints(relativePath) {
|
|
579
|
+
const segments = pathSegments(relativePath);
|
|
580
|
+
let score = 0;
|
|
581
|
+
for (const segment of segments) {
|
|
582
|
+
if (["app", "apps", "site", "sites", "web", "www", "service", "services"].includes(segment)) {
|
|
583
|
+
score += 12;
|
|
584
|
+
}
|
|
585
|
+
if (["docs", "example", "examples", "test", "tests", "storybook"].includes(segment)) {
|
|
586
|
+
score -= 18;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return score;
|
|
590
|
+
}
|
|
591
|
+
async function scoreCandidate(dir, relativePath) {
|
|
592
|
+
let score = scorePathHints(relativePath);
|
|
593
|
+
score += await manifestCount(dir) * 20;
|
|
594
|
+
if (relativePath) {
|
|
595
|
+
score += 6;
|
|
596
|
+
}
|
|
597
|
+
return score;
|
|
598
|
+
}
|
|
599
|
+
async function collectCandidates(rootDir, currentDir, depth, candidates) {
|
|
600
|
+
const relativePath = toPosixPath(relative(rootDir, currentDir));
|
|
601
|
+
if (await manifestCount(currentDir) > 0) {
|
|
602
|
+
candidates.push({
|
|
603
|
+
path: relativePath,
|
|
604
|
+
score: await scoreCandidate(currentDir, relativePath)
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
if (depth >= 6) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const entries = await readdir(currentDir, { withFileTypes: true }).catch(() => []);
|
|
611
|
+
for (const entry of entries) {
|
|
612
|
+
if (!entry.isDirectory() || IGNORED_DIRS.has(entry.name)) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
await collectCandidates(rootDir, join(currentDir, entry.name), depth + 1, candidates);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
async function resolveDockerRootDirectory(contextDir, rootDirectory, localPath) {
|
|
619
|
+
const hasExplicitRootDirectory = typeof rootDirectory === "string";
|
|
620
|
+
const normalized = normalizeDockerRootDirectory(rootDirectory, localPath);
|
|
621
|
+
if (hasExplicitRootDirectory) {
|
|
622
|
+
return normalized;
|
|
623
|
+
}
|
|
624
|
+
if (normalized) {
|
|
625
|
+
return normalized;
|
|
626
|
+
}
|
|
627
|
+
const candidates = [];
|
|
628
|
+
await collectCandidates(contextDir, contextDir, 0, candidates);
|
|
629
|
+
if (candidates.length === 0) {
|
|
630
|
+
return "";
|
|
631
|
+
}
|
|
632
|
+
candidates.sort((left, right) => right.score - left.score);
|
|
633
|
+
const bestNonRoot = candidates.find((candidate) => candidate.path);
|
|
634
|
+
if (bestNonRoot && bestNonRoot.score > 0) {
|
|
635
|
+
return bestNonRoot.path;
|
|
636
|
+
}
|
|
637
|
+
return candidates[0]?.path ?? "";
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// ../../packages/adapters/src/runtime/docker-build-plan.ts
|
|
641
|
+
var DOCKER_BUILD_EVENT_PREFIX = "[openship-build]";
|
|
642
|
+
var INLINE_BUILD_ENV_EXCLUDES = /* @__PURE__ */ new Set(["FORCE_COLOR", "TERM"]);
|
|
643
|
+
function formatDockerBuildEvent(step, status) {
|
|
644
|
+
return `${DOCKER_BUILD_EVENT_PREFIX} step=${step} status=${status}`;
|
|
645
|
+
}
|
|
646
|
+
function normalizeRelativePath(value) {
|
|
647
|
+
const normalized = value?.trim().replace(/^\.\//, "").replace(/^\/+|\/+$/g, "");
|
|
648
|
+
if (!normalized || normalized === ".") {
|
|
649
|
+
return "";
|
|
650
|
+
}
|
|
651
|
+
return normalized;
|
|
652
|
+
}
|
|
653
|
+
function builderSourceDir(rootDirectory) {
|
|
654
|
+
const normalized = normalizeDockerRootDirectory(rootDirectory);
|
|
655
|
+
return normalized ? `/workspace/${normalized}` : "/workspace";
|
|
656
|
+
}
|
|
657
|
+
function buildEnvPrefix(envVars) {
|
|
658
|
+
const assignments = Object.entries(envVars).filter(([key]) => !INLINE_BUILD_ENV_EXCLUDES.has(key)).filter(([key]) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(key)).map(([key, value]) => `${key}=${sq(value)}`);
|
|
659
|
+
if (!envVars.NO_COLOR) {
|
|
660
|
+
assignments.push("NO_COLOR='1'");
|
|
661
|
+
}
|
|
662
|
+
if (assignments.length === 0) {
|
|
663
|
+
return "";
|
|
664
|
+
}
|
|
665
|
+
return `export ${assignments.join(" ")} && `;
|
|
666
|
+
}
|
|
667
|
+
function buildRunCommand(command, envPrefix) {
|
|
668
|
+
return `${envPrefix}${command}`;
|
|
669
|
+
}
|
|
670
|
+
function runtimeCopyDirectives(config, sourceDir) {
|
|
671
|
+
if (config.productionPaths && config.productionPaths.length > 0) {
|
|
672
|
+
return config.productionPaths.map((path) => {
|
|
673
|
+
const normalized = normalizeRelativePath(path);
|
|
674
|
+
const source = normalized ? `${sourceDir}/${normalized}` : sourceDir;
|
|
675
|
+
const target = normalized ? `/app/${normalized}` : "/app";
|
|
676
|
+
return `COPY --from=builder ${source} ${target}`;
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
return [`COPY --from=builder ${sourceDir} /app`];
|
|
680
|
+
}
|
|
681
|
+
function needsMultiStage(config) {
|
|
682
|
+
return config.buildImage !== config.runtimeImage;
|
|
683
|
+
}
|
|
684
|
+
function workspacePrepareRunLine(config, envPrefix, workspacePrepare) {
|
|
685
|
+
const pmEnsure = packageManagerEnsureCommand(config.packageManager);
|
|
686
|
+
const body = pmEnsure ? `${pmEnsure} && ${workspacePrepare}` : workspacePrepare;
|
|
687
|
+
return `RUN ${envPrefix}${body}`;
|
|
688
|
+
}
|
|
689
|
+
function installBuildRunLine(config, envPrefix) {
|
|
690
|
+
const steps = [];
|
|
691
|
+
const pmEnsure = packageManagerEnsureCommand(config.packageManager);
|
|
692
|
+
if (pmEnsure && (config.installCommand || config.buildCommand)) {
|
|
693
|
+
steps.push(pmEnsure);
|
|
694
|
+
}
|
|
695
|
+
if (config.installCommand) {
|
|
696
|
+
steps.push(`printf '${formatDockerBuildEvent("install", "running")}\\n'`);
|
|
697
|
+
steps.push(buildRunCommand(config.installCommand, envPrefix));
|
|
698
|
+
steps.push(`printf '${formatDockerBuildEvent("install", "completed")}\\n'`);
|
|
699
|
+
}
|
|
700
|
+
if (config.buildCommand) {
|
|
701
|
+
steps.push(`printf '${formatDockerBuildEvent("build", "running")}\\n'`);
|
|
702
|
+
steps.push(buildRunCommand(config.buildCommand, envPrefix));
|
|
703
|
+
steps.push(`printf '${formatDockerBuildEvent("build", "completed")}\\n'`);
|
|
704
|
+
}
|
|
705
|
+
return steps.length > 0 ? `RUN ${steps.join(" && ")}` : null;
|
|
706
|
+
}
|
|
707
|
+
function isPhpRuntime(config) {
|
|
708
|
+
return /^php:/.test(config.runtimeImage);
|
|
709
|
+
}
|
|
710
|
+
var PHP_NGINX_TEMPLATE_LINES = [
|
|
711
|
+
"server {",
|
|
712
|
+
" listen ${PORT} default_server;",
|
|
713
|
+
" root /app/public;",
|
|
714
|
+
" index index.php index.html;",
|
|
715
|
+
" location / { try_files $uri $uri/ /index.php?$query_string; }",
|
|
716
|
+
" location ~ \\.php$ {",
|
|
717
|
+
" fastcgi_pass 127.0.0.1:9000;",
|
|
718
|
+
" fastcgi_index index.php;",
|
|
719
|
+
" include fastcgi_params;",
|
|
720
|
+
" fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;",
|
|
721
|
+
" }",
|
|
722
|
+
"}"
|
|
723
|
+
];
|
|
724
|
+
function generatePhpDockerfile(config) {
|
|
725
|
+
const sourceDir = builderSourceDir(
|
|
726
|
+
normalizeDockerRootDirectory(config.rootDirectory, config.localPath)
|
|
727
|
+
);
|
|
728
|
+
const envPrefix = buildEnvPrefix(config.envVars);
|
|
729
|
+
const stepsLine = installBuildRunLine(config, envPrefix);
|
|
730
|
+
const nginxTemplate = PHP_NGINX_TEMPLATE_LINES.map((line) => `'${line}'`).join(" ");
|
|
731
|
+
const lines = [
|
|
732
|
+
`FROM ${config.buildImage} AS builder`,
|
|
733
|
+
`WORKDIR /workspace`,
|
|
734
|
+
`COPY . /workspace`,
|
|
735
|
+
// php:*-cli ships no Composer — pull the binary from the official image.
|
|
736
|
+
`COPY --from=composer:2 /usr/bin/composer /usr/bin/composer`,
|
|
737
|
+
// pdo_mysql is the common denominator (Laravel/Symfony + MySQL/MariaDB);
|
|
738
|
+
// app-specific extensions are a per-project follow-on.
|
|
739
|
+
`RUN docker-php-ext-install pdo_mysql > /dev/null`,
|
|
740
|
+
`WORKDIR ${sourceDir}`
|
|
741
|
+
];
|
|
742
|
+
if (stepsLine) lines.push(stepsLine);
|
|
743
|
+
lines.push(
|
|
744
|
+
`FROM ${config.runtimeImage} AS runtime`,
|
|
745
|
+
`RUN apt-get update && apt-get install -y --no-install-recommends nginx gettext-base && rm -rf /var/lib/apt/lists/* && rm -f /etc/nginx/sites-enabled/default && docker-php-ext-install pdo_mysql > /dev/null`,
|
|
746
|
+
`COPY --from=builder ${sourceDir} /app`,
|
|
747
|
+
`WORKDIR /app`,
|
|
748
|
+
`RUN printf '%s\\n' ${nginxTemplate} > /etc/nginx/app.conf.template`,
|
|
749
|
+
`EXPOSE ${config.port}`
|
|
750
|
+
);
|
|
751
|
+
if (config.startCommand) {
|
|
752
|
+
lines.push(`CMD ["sh", "-c", ${JSON.stringify(config.startCommand)}]`);
|
|
753
|
+
}
|
|
754
|
+
return lines.join("\n");
|
|
755
|
+
}
|
|
756
|
+
function staticNginxTemplateLines(port) {
|
|
757
|
+
return [
|
|
758
|
+
"server {",
|
|
759
|
+
` listen ${port} default_server;`,
|
|
760
|
+
" root /usr/share/nginx/html;",
|
|
761
|
+
" index index.html;",
|
|
762
|
+
" location / { try_files $uri $uri/ /index.html; }",
|
|
763
|
+
"}"
|
|
764
|
+
];
|
|
765
|
+
}
|
|
766
|
+
function generateStaticDockerfile(config) {
|
|
767
|
+
const sourceDir = builderSourceDir(
|
|
768
|
+
normalizeDockerRootDirectory(config.rootDirectory, config.localPath)
|
|
769
|
+
);
|
|
770
|
+
const envPrefix = buildEnvPrefix(config.envVars);
|
|
771
|
+
const workspacePrepare = config.workspacePrepareCommand?.trim();
|
|
772
|
+
const output = normalizeRelativePath(config.outputDirectory);
|
|
773
|
+
const outputPath = output ? `${sourceDir}/${output}` : sourceDir;
|
|
774
|
+
const nginxTemplate = staticNginxTemplateLines(config.port).map((line) => `'${line}'`).join(" ");
|
|
775
|
+
const lines = [
|
|
776
|
+
`FROM ${config.buildImage} AS builder`,
|
|
777
|
+
`WORKDIR /workspace`,
|
|
778
|
+
`COPY . /workspace`
|
|
779
|
+
];
|
|
780
|
+
if (workspacePrepare) {
|
|
781
|
+
lines.push(workspacePrepareRunLine(config, envPrefix, workspacePrepare));
|
|
782
|
+
}
|
|
783
|
+
lines.push(`WORKDIR ${sourceDir}`);
|
|
784
|
+
const stepsLine = installBuildRunLine(config, envPrefix);
|
|
785
|
+
if (stepsLine) lines.push(stepsLine);
|
|
786
|
+
lines.push(
|
|
787
|
+
`FROM nginx:alpine AS runtime`,
|
|
788
|
+
`RUN rm -f /etc/nginx/conf.d/default.conf`,
|
|
789
|
+
`COPY --from=builder ${outputPath} /usr/share/nginx/html`,
|
|
790
|
+
`RUN printf '%s\\n' ${nginxTemplate} > /etc/nginx/conf.d/app.conf`,
|
|
791
|
+
`EXPOSE ${config.port}`,
|
|
792
|
+
`CMD ["nginx", "-g", "daemon off;"]`
|
|
793
|
+
);
|
|
794
|
+
return lines.join("\n");
|
|
795
|
+
}
|
|
796
|
+
function generateDockerfile(config) {
|
|
797
|
+
if (config.isStatic) {
|
|
798
|
+
return generateStaticDockerfile(config);
|
|
799
|
+
}
|
|
800
|
+
if (isPhpRuntime(config) && needsMultiStage(config)) {
|
|
801
|
+
return generatePhpDockerfile(config);
|
|
802
|
+
}
|
|
803
|
+
const sourceDir = builderSourceDir(
|
|
804
|
+
normalizeDockerRootDirectory(config.rootDirectory, config.localPath)
|
|
805
|
+
);
|
|
806
|
+
const multiStage = needsMultiStage(config);
|
|
807
|
+
const envPrefix = buildEnvPrefix(config.envVars);
|
|
808
|
+
const workspacePrepare = config.workspacePrepareCommand?.trim();
|
|
809
|
+
const lines = multiStage ? [
|
|
810
|
+
`FROM ${config.buildImage} AS builder`,
|
|
811
|
+
`WORKDIR /workspace`,
|
|
812
|
+
`COPY . /workspace`
|
|
813
|
+
] : [
|
|
814
|
+
`FROM ${config.runtimeImage}`,
|
|
815
|
+
`WORKDIR /workspace`,
|
|
816
|
+
`COPY . /workspace`
|
|
817
|
+
];
|
|
818
|
+
if (workspacePrepare) {
|
|
819
|
+
lines.push(workspacePrepareRunLine(config, envPrefix, workspacePrepare));
|
|
820
|
+
}
|
|
821
|
+
lines.push(`WORKDIR ${sourceDir}`);
|
|
822
|
+
const stepsLine = installBuildRunLine(config, envPrefix);
|
|
823
|
+
if (stepsLine) {
|
|
824
|
+
lines.push(stepsLine);
|
|
825
|
+
}
|
|
826
|
+
if (multiStage) {
|
|
827
|
+
lines.push(`FROM ${config.runtimeImage} AS runtime`);
|
|
828
|
+
lines.push(...runtimeCopyDirectives(config, sourceDir));
|
|
829
|
+
lines.push(`WORKDIR /app`);
|
|
830
|
+
}
|
|
831
|
+
lines.push(`EXPOSE ${config.port}`);
|
|
832
|
+
if (config.startCommand) {
|
|
833
|
+
lines.push(`CMD ["sh", "-c", ${JSON.stringify(config.startCommand)}]`);
|
|
834
|
+
}
|
|
835
|
+
return lines.join("\n");
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// ../../packages/adapters/src/runtime/docker-build-context.ts
|
|
839
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
840
|
+
import { spawn } from "child_process";
|
|
841
|
+
import { access as access2, mkdtemp, readdir as readdir2, readFile, rm, writeFile } from "fs/promises";
|
|
842
|
+
import { tmpdir } from "os";
|
|
843
|
+
import { join as join2, relative as relative2, sep as sep2 } from "path";
|
|
844
|
+
var GIT_CLONE_IDLE_TIMEOUT_MS = 5 * 6e4;
|
|
845
|
+
var GIT_CHECKOUT_IDLE_TIMEOUT_MS = 6e4;
|
|
846
|
+
function spawnGit(args, opts) {
|
|
847
|
+
return new Promise((resolve, reject) => {
|
|
848
|
+
const child = spawn("git", args, {
|
|
849
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
850
|
+
env: {
|
|
851
|
+
...process.env,
|
|
852
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
853
|
+
GIT_ASKPASS: "/bin/echo",
|
|
854
|
+
...opts.env
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
let stderr = "";
|
|
858
|
+
const emit = opts.onLog;
|
|
859
|
+
const flushLine = (line) => {
|
|
860
|
+
const trimmed = line.trimEnd();
|
|
861
|
+
if (!trimmed) return;
|
|
862
|
+
if (emit) {
|
|
863
|
+
emit({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), message: trimmed, level: "info" });
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
let idleTimer;
|
|
867
|
+
const armIdle = () => {
|
|
868
|
+
clearTimeout(idleTimer);
|
|
869
|
+
idleTimer = setTimeout(() => {
|
|
870
|
+
child.kill("SIGKILL");
|
|
871
|
+
reject(
|
|
872
|
+
new Error(
|
|
873
|
+
`git ${args.find((a) => !a.startsWith("-")) ?? "command"} stalled \u2014 no progress for ${Math.round(
|
|
874
|
+
opts.timeoutMs / 1e3
|
|
875
|
+
)}s`
|
|
876
|
+
)
|
|
877
|
+
);
|
|
878
|
+
}, opts.timeoutMs);
|
|
879
|
+
};
|
|
880
|
+
child.stdout?.on("data", (buf) => {
|
|
881
|
+
armIdle();
|
|
882
|
+
for (const ln of buf.toString().split(/\r?\n/)) flushLine(ln);
|
|
883
|
+
});
|
|
884
|
+
child.stderr?.on("data", (buf) => {
|
|
885
|
+
armIdle();
|
|
886
|
+
const text = buf.toString();
|
|
887
|
+
stderr += text;
|
|
888
|
+
for (const ln of text.split(/\r?\n/)) flushLine(ln);
|
|
889
|
+
});
|
|
890
|
+
armIdle();
|
|
891
|
+
child.on("error", (err) => {
|
|
892
|
+
clearTimeout(idleTimer);
|
|
893
|
+
reject(err);
|
|
894
|
+
});
|
|
895
|
+
child.on("close", (code) => {
|
|
896
|
+
clearTimeout(idleTimer);
|
|
897
|
+
if (code === 0) resolve();
|
|
898
|
+
else reject(new Error(`git exited with code ${code}: ${stderr.trim().slice(-500) || "no stderr"}`));
|
|
899
|
+
});
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
var GENERATED_DOCKERFILE_NAME = "Dockerfile.openship";
|
|
903
|
+
function toPosixPath2(value) {
|
|
904
|
+
return value.split(sep2).filter(Boolean).join("/");
|
|
905
|
+
}
|
|
906
|
+
async function loadDockerignoreMatcher(rootPath) {
|
|
907
|
+
try {
|
|
908
|
+
return (0, import_ignore.default)().add(await readFile(join2(rootPath, ".dockerignore"), "utf-8"));
|
|
909
|
+
} catch {
|
|
910
|
+
return void 0;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
async function applyDockerignore(contextDir) {
|
|
914
|
+
const matcher = await loadDockerignoreMatcher(contextDir);
|
|
915
|
+
if (!matcher) return;
|
|
916
|
+
const prune = async (currentPath) => {
|
|
917
|
+
const entries = await readdir2(currentPath, { withFileTypes: true });
|
|
918
|
+
await Promise.all(
|
|
919
|
+
entries.map(async (entry) => {
|
|
920
|
+
const absolutePath = join2(currentPath, entry.name);
|
|
921
|
+
const relativePath = relative2(contextDir, absolutePath);
|
|
922
|
+
if (matcher.ignores(toPosixPath2(relativePath))) {
|
|
923
|
+
await rm(absolutePath, { recursive: true, force: true });
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (entry.isDirectory()) await prune(absolutePath);
|
|
927
|
+
})
|
|
928
|
+
);
|
|
929
|
+
};
|
|
930
|
+
await prune(contextDir);
|
|
931
|
+
}
|
|
932
|
+
async function materializeLocalSource(sourcePath, targetPath) {
|
|
933
|
+
const { args, cleanup } = await prepareSourceTarArgs(sourcePath);
|
|
934
|
+
try {
|
|
935
|
+
await new Promise((resolve, reject) => {
|
|
936
|
+
const create = spawn("tar", args, { env: getTarCreateEnv() });
|
|
937
|
+
const extract = spawn("tar", ["-xzf", "-", "-C", targetPath]);
|
|
938
|
+
let err = "";
|
|
939
|
+
create.stderr.on("data", (d) => err += d.toString());
|
|
940
|
+
extract.stderr.on("data", (d) => err += d.toString());
|
|
941
|
+
create.on("error", reject);
|
|
942
|
+
extract.on("error", reject);
|
|
943
|
+
create.stdout.pipe(extract.stdin);
|
|
944
|
+
extract.on(
|
|
945
|
+
"close",
|
|
946
|
+
(code) => code === 0 ? resolve() : reject(new Error(`context materialize failed (tar ${code}): ${err.trim().slice(-500)}`))
|
|
947
|
+
);
|
|
948
|
+
});
|
|
949
|
+
} finally {
|
|
950
|
+
await cleanup();
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
async function resolveDockerfileName(contextDir, rootDirectory, explicitDockerfilePath) {
|
|
954
|
+
const candidates = resolveDockerfileCandidates(rootDirectory, explicitDockerfilePath);
|
|
955
|
+
for (const candidate of candidates) {
|
|
956
|
+
const candidatePath = join2(contextDir, ...candidate.split("/"));
|
|
957
|
+
const exists = await access2(candidatePath).then(() => true).catch(() => false);
|
|
958
|
+
if (exists) {
|
|
959
|
+
return candidate;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return null;
|
|
963
|
+
}
|
|
964
|
+
async function cloneGitSource(config, targetPath, onLog) {
|
|
965
|
+
let sshMaterial;
|
|
966
|
+
if (config.gitSsh) {
|
|
967
|
+
sshMaterial = await materializeGitSsh(
|
|
968
|
+
localGitSshWriter(),
|
|
969
|
+
await mkdtemp(join2(tmpdir(), "opsh-ghkey-")),
|
|
970
|
+
config.gitSsh
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
const { cloneUrl, env: gitEnv, credArgs } = assembleGitClone({
|
|
974
|
+
repoUrl: config.repoUrl,
|
|
975
|
+
gitToken: config.gitToken,
|
|
976
|
+
ssh: sshMaterial
|
|
977
|
+
});
|
|
978
|
+
try {
|
|
979
|
+
await spawnGit(
|
|
980
|
+
[
|
|
981
|
+
...credArgs,
|
|
982
|
+
"clone",
|
|
983
|
+
"--progress",
|
|
984
|
+
"--depth",
|
|
985
|
+
config.commitSha ? "50" : "1",
|
|
986
|
+
"--branch",
|
|
987
|
+
config.branch,
|
|
988
|
+
cloneUrl,
|
|
989
|
+
targetPath
|
|
990
|
+
],
|
|
991
|
+
{ timeoutMs: GIT_CLONE_IDLE_TIMEOUT_MS, onLog, env: gitEnv }
|
|
992
|
+
);
|
|
993
|
+
if (config.commitSha) {
|
|
994
|
+
await spawnGit([...credArgs, "-C", targetPath, "checkout", config.commitSha], {
|
|
995
|
+
timeoutMs: GIT_CHECKOUT_IDLE_TIMEOUT_MS,
|
|
996
|
+
onLog,
|
|
997
|
+
env: gitEnv
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
await rm(join2(targetPath, ".git"), { recursive: true, force: true });
|
|
1001
|
+
} finally {
|
|
1002
|
+
await sshMaterial?.cleanup();
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
async function prepareSourceTree(config, opts) {
|
|
1006
|
+
const contextDir = await mkdtemp(join2(tmpdir(), "openship-docker-context-"));
|
|
1007
|
+
try {
|
|
1008
|
+
if (config.localPath) {
|
|
1009
|
+
await materializeLocalSource(config.localPath, contextDir);
|
|
1010
|
+
} else {
|
|
1011
|
+
await cloneGitSource(config, contextDir, opts?.onLog);
|
|
1012
|
+
}
|
|
1013
|
+
await applyDockerignore(contextDir);
|
|
1014
|
+
return {
|
|
1015
|
+
contextDir,
|
|
1016
|
+
cleanup: async () => {
|
|
1017
|
+
await rm(contextDir, { recursive: true, force: true }).catch(() => {
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
await rm(contextDir, { recursive: true, force: true }).catch(() => {
|
|
1023
|
+
});
|
|
1024
|
+
throw error;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
async function resolveServiceDockerfile(contextDir, config, opts) {
|
|
1028
|
+
const requireRepositoryDockerfile = opts?.requireRepositoryDockerfile ?? false;
|
|
1029
|
+
const generatedName = opts?.generatedName ?? GENERATED_DOCKERFILE_NAME;
|
|
1030
|
+
const resolvedRootDirectory = await resolveDockerRootDirectory(
|
|
1031
|
+
contextDir,
|
|
1032
|
+
config.rootDirectory,
|
|
1033
|
+
config.localPath
|
|
1034
|
+
);
|
|
1035
|
+
const repositoryDockerfileName = await resolveDockerfileName(
|
|
1036
|
+
contextDir,
|
|
1037
|
+
resolvedRootDirectory,
|
|
1038
|
+
config.dockerfilePath
|
|
1039
|
+
);
|
|
1040
|
+
const hasRepositoryDockerfile = repositoryDockerfileName !== null;
|
|
1041
|
+
if (!hasRepositoryDockerfile && requireRepositoryDockerfile) {
|
|
1042
|
+
const expectedDockerfile = config.dockerfilePath?.trim() || "Dockerfile";
|
|
1043
|
+
throw new Error(
|
|
1044
|
+
`No Dockerfile found for this build context. Expected ${expectedDockerfile}${config.rootDirectory ? ` under ${config.rootDirectory}` : ""}.`
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
if (!hasRepositoryDockerfile) {
|
|
1048
|
+
await writeFile(
|
|
1049
|
+
join2(contextDir, generatedName),
|
|
1050
|
+
generateDockerfile({
|
|
1051
|
+
...config,
|
|
1052
|
+
rootDirectory: resolvedRootDirectory
|
|
1053
|
+
}),
|
|
1054
|
+
"utf-8"
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
const contextEntries = await readdir2(contextDir);
|
|
1058
|
+
return {
|
|
1059
|
+
contextEntries,
|
|
1060
|
+
dockerfileName: repositoryDockerfileName ?? generatedName,
|
|
1061
|
+
rootDirectory: resolvedRootDirectory,
|
|
1062
|
+
usesRepositoryDockerfile: hasRepositoryDockerfile
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
async function createDockerBuildContext(config, opts) {
|
|
1066
|
+
const tree = await prepareSourceTree(config, { onLog: opts?.onLog });
|
|
1067
|
+
try {
|
|
1068
|
+
const resolved = await resolveServiceDockerfile(tree.contextDir, config, {
|
|
1069
|
+
requireRepositoryDockerfile: opts?.requireRepositoryDockerfile
|
|
1070
|
+
});
|
|
1071
|
+
return {
|
|
1072
|
+
contextDir: tree.contextDir,
|
|
1073
|
+
contextEntries: resolved.contextEntries,
|
|
1074
|
+
dockerfileName: resolved.dockerfileName,
|
|
1075
|
+
rootDirectory: resolved.rootDirectory,
|
|
1076
|
+
usesRepositoryDockerfile: resolved.usesRepositoryDockerfile,
|
|
1077
|
+
cleanup: tree.cleanup
|
|
1078
|
+
};
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
await tree.cleanup();
|
|
1081
|
+
throw error;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
export {
|
|
1086
|
+
normalizeDockerRelativePath,
|
|
1087
|
+
resolveDockerfileCandidates,
|
|
1088
|
+
generateDockerfile,
|
|
1089
|
+
prepareSourceTree,
|
|
1090
|
+
resolveServiceDockerfile,
|
|
1091
|
+
createDockerBuildContext
|
|
1092
|
+
};
|