fix: complete server pull deployment setup
ForgeFlow quality gate / quality (push) Canceled after 0s
ForgeFlow quality gate / quality (push) Canceled after 0s
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
ForgeFlow is een Windows-desktopapp voor wie Git, Gitea en eigen Docker- of Unraid-servers gebruikt. Je ziet in één werkruimte wat lokaal gewijzigd is, wat op Gitea staat en welke exacte commit op de server draait. ForgeFlow begeleidt je daarna veilig door review, commit, push, deployment en verificatie.
|
||||
|
||||
> Huidige release: **0.10.9** · [download de laatste Windows-release](https://gitea.itworx.tech/Jens/ForgeFlow/releases/latest)
|
||||
> Huidige release: **0.10.10** · [download de laatste Windows-release](https://gitea.itworx.tech/Jens/ForgeFlow/releases/latest)
|
||||
|
||||

|
||||
|
||||
|
||||
+12
-11
@@ -1,4 +1,4 @@
|
||||
ForgeFlow 0.10.9 source manifest
|
||||
ForgeFlow 0.10.10 source manifest
|
||||
SHA-256 BYTES PATH
|
||||
(The manifest excludes itself, dependencies and generated release artifacts.)
|
||||
cedceb71eb846d99c7c4019031833c1c7f93b84a1c6073aec7d2435dc744ca3d 703 .gitea/workflows/quality.yml
|
||||
@@ -34,6 +34,7 @@ a0cd06a96f23a94e118feb012be0fa1ac51345951cb2ba8e67fb8c889c4c342a 5007
|
||||
f79908fb3dad98c38030c6e6be7c79a1999e0478ed9c2496923891954438daa1 4581 docs/RELEASE_AUDIT_0.6.0.md
|
||||
979a0b8e129979be6b265e8571d0a3c1e9ddd4ddb6b0bf55ae748d3478e51854 2296 docs/RELEASE_NOTES_0.10.0.md
|
||||
0eb44bda2209a5979a6ac693ac4cd4d235c0015031e54b9e895990f37bf60054 1433 docs/RELEASE_NOTES_0.10.1.md
|
||||
5d3240169765e3fb1d3cd391d09547101227e76dd4670ee46be8ca3a21553a03 894 docs/RELEASE_NOTES_0.10.10.md
|
||||
8d713471a437a8a55b00d7e1dd95290680862107bc4e586cf27d727f6274e46c 577 docs/RELEASE_NOTES_0.10.2.md
|
||||
0942fb2c4a4f972296423b5232687f7389e2c6417a9d48a3244beef9dec907b9 1164 docs/RELEASE_NOTES_0.10.3.md
|
||||
8f4a0fe6dc250ae210cc2fc1c57c46091822ae6c2a58caa76e0091f255f9f30d 775 docs/RELEASE_NOTES_0.10.4.md
|
||||
@@ -103,14 +104,14 @@ c230b931abf2293d2d44b7a69b94c35f1142c093cc46b88739a0de5cbd6d1896 1532
|
||||
4a561ead5ba7cdfaf4efce91842a4308c5f2a77980205879d83835efb8a579db 1067 LICENSE
|
||||
970c2afb09f2b9d1e97a5a92c0bd824c054b72298c5a5d234396d5e34de065ae 13870 main.cjs
|
||||
91a984a89dd57a084b9a2331763cacdb061582fb590f13df379d92c1a77a2ee1 352 OVERLAY-INSTRUCTIONS.md
|
||||
f79efd5414d35bbcd1476956db1feaab43c3a8f6d22325ef1020a93c34f8e2ee 179806 package-lock.json
|
||||
2b9c81b45209f775ced9af95b43e5a32e3bce9369406881c1aba1fdbf93ee402 5664 package.json
|
||||
9c2567281d405b608b5381d79360288601a2a4211bdacf5a8cae990caa952f98 179808 package-lock.json
|
||||
da986418af193d1334e4ab9e09a5dab41ebe32aace73127a145be834ef20bbd9 5704 package.json
|
||||
2a597a5704c576783b8a72407fbc377fa7506b36a4596ea7f7bce126e394f837 1326 playwright.config.mjs
|
||||
69318fdf054be7aa2fe86ead9847da9da65745d8d5de548c8346f3ba0afc4892 12175 preload.cjs
|
||||
abe5dd6fd68f2970cd19ef134094907c67219061d8fe9a1a08324c78de4ad437 484 PUBLISH-AND-ENABLE-UPDATE.cmd
|
||||
f018383f755352ca448e2ebb1e19b1dba412a3eb793d61e64b02953e300754fd 10538 Publish-ForgeFlow-Release.ps1
|
||||
688fff7d2c989adb97ebb7fae38962656b70304a0aa5d27433c56adf7f136de0 4196 Publish-Missing-Binary-Release.ps1
|
||||
b1a5837a2087cc9a160f1e60c7cdfd750668314ddfdb42ce11a4368151e8d048 10313 README.md
|
||||
0438b8de9bb3409543e4f3bc121bd3ccc094a5a879655db11aef1403c323f96e 10314 README.md
|
||||
8f8dc5ff0c2967563457e4027d795f6f515253e0a459ecca5a9f5bcf0b658870 14650 reports/architecture-audit.json
|
||||
5e923f0096895f588fe66b187779cdb7cb5af6644010c6dcc4d55876930ae376 1220 reports/architecture-audit.md
|
||||
509c7bcff5280349bd9f45ed6151f70372bad7010a9ea582c13e2ccab91fe0cd 6272 scripts/acceptance.mjs
|
||||
@@ -126,7 +127,7 @@ b83d443f5724ac15393567f3a688aed8315fbe3e5966832c864a9466e0669464 8102
|
||||
c76507857292c5713e1c699cf02e24b80265da39af2cecd148034bdb874adbb6 5246 scripts/test-authenticode-chain.ps1
|
||||
4393f7dc5f417e6d601a68238f4e26791799a3634acec228fe4d79deaee85eb5 3109 scripts/validate-installed-connections.cjs
|
||||
e6127e1e62f39c70ddb1abf72f4d7e7b8e3f19ff1f219e1a3660353c2e0cdfac 2411 scripts/verify-release-signatures.mjs
|
||||
b35a8701bba54338cce22c0afc3934f5c9747bcfedd85475ccde2c1b7352743d 20272 scripts/verify.mjs
|
||||
2a80bfc448aed0a50597947afc75ae42c6830d5bcfd5c5f712957c85268b23d3 20660 scripts/verify.mjs
|
||||
0b9f03ba3c67ff7cdb2916a902ad8ce25e81a7c90b210e4ae52d2ad029efabf3 2353 scripts/write-release-checksums.mjs
|
||||
619515f524cb89960370ffcbd3fafd3c0e178b95f69c5868b1dd44777f23ec1e 2081 setup-windows.ps1
|
||||
dd613d04b366f2cd071a1685a414016a5fb008082ed1b4cb8b24b79c100f640a 2412 src/main/audit-service.cjs
|
||||
@@ -155,11 +156,11 @@ fc7a5a2a42579a311f60a96bec4776942bf3f63428928820029b0dcf4874d1aa 2745
|
||||
17e2a53f61cd7faba461b9f332967143087eaac95b72001462292976278ca305 7782 src/main/repository-service.cjs
|
||||
52b6d88ed1f5c904a13cdde92e5f96d1e2b5971ceef49862152197353cdc6490 27928 src/main/server-inventory.cjs
|
||||
afef3841a3948b2121f8fba809aae4ea3da71bd2fda86973ba50200a5b1f89b2 14894 src/main/ssh-service.cjs
|
||||
560084e3f10062f8f21b62f1cc50050350558e9505ab96ed235c6ab377da2546 25774 src/main/unraid-access-methods.cjs
|
||||
d85780a19cac9ed5c89721a25916c35235e76012159363ff61924dba4215d171 25801 src/main/unraid-access-methods.cjs
|
||||
2ede80cd1565a7f2c282cc58d35dc0889d58d7465346bc723026b9c8be4df0ac 9501 src/main/unraid-deploy-key-host.cjs
|
||||
803a079499f7b8148495209dea43b505f6eb9bb587de19e82054e47183186c6e 30461 src/main/unraid-deployment-methods.cjs
|
||||
2e63fdc0be0bf4d8e5c3d7d45ff5811d786b0821a08b82f511f1301696e12c9d 17157 src/main/unraid-deployment-service.cjs
|
||||
0f95a26b7206603c4ea12d3ad4733b8d5730ca18a344ffad49e2a3ffe46a0699 37780 src/main/unraid-inventory-methods.cjs
|
||||
b8a6ef72cfd5c5405e3f1a48848b5ef1575ca82ebbe8cad816dff0dc7335c8ec 17184 src/main/unraid-deployment-service.cjs
|
||||
c3439857c985a44f8298f76aca46ad71f5fa30eabb32ddca9d18fc4f65dc4a38 38809 src/main/unraid-inventory-methods.cjs
|
||||
2c0cf07921ca7ee5a9085ced44498c2e6798e5cc1e8a5ecf704c3cecabe39a25 27607 src/main/unraid-preflight-methods.cjs
|
||||
d45220176aed72d692f9ae5534f9d40bcc359a2d08e025e74a3b3b505b8b9ed4 16559 src/main/unraid-runtime-methods.cjs
|
||||
d4b3a07eeca687a49544a94ea574f7c6f7e0bc3aa9311b614d5244a468ca4a1b 11307 src/main/unraid-state-methods.cjs
|
||||
@@ -181,7 +182,7 @@ eef2f269ba4fbb76bf66ad328d481b461255d0acb753b30878dd4d4eaac57dc6 6924
|
||||
a84da5aecbb16ce7983dba1f6d6aab1bf47b2e9a87c2933fa1afb8123f7ef7d6 1497 src/renderer/index.html
|
||||
06180d9656dd254edfb6949c397f8e313954fc560ddcb22b3a35fce3c3e35655 21350 src/renderer/mock-bridge.js
|
||||
5a2c0616a8a0ba23c182a8928abdf40a69612ce34c4cba47b1fd5f21fe5843d4 28617 src/renderer/mock-deployment-bridge.js
|
||||
4ace18f1756da5f9ec3e4cf40a141cf3268e796b1fd9d788bd3ddab3652c7c04 20032 src/renderer/mock-repository-bridge.js
|
||||
041daf59931aff37075d9ee238bdf9c397c4dd799580540f30216276f95cfd7a 20033 src/renderer/mock-repository-bridge.js
|
||||
94fa265c2fe9ca8d644f0ce9b620b6f85d9b25dca5802c4e9195b66dcbe80120 6522 src/renderer/operations.js
|
||||
1fc4c28859f7283aa2b771a50f1fd26addc8862b97a09250f806930ab4483f7a 79874 src/renderer/styles.css
|
||||
4fbd3d7af3279a02c1da508758f6c7d9ca8de3d82f869be8439030ec81e8d941 101429 src/renderer/views.js
|
||||
@@ -198,7 +199,7 @@ f8853dce6fdf360d5df2fbe2b6df3e5687630c807fee5ba8436679b34ec737ea 2436
|
||||
058aeaa5d9bfe377c7e322f213c7871ecc4151b5d08ef790992f4ee28d857658 743 START-FORGEFLOW-OVERLAY.ps1
|
||||
f5b0ea887fcdeadec78c1ad49b0ec7979723562f5c0b730703acb77a37281ee0 1009 tests/acceptance.test.mjs
|
||||
a4e5947204ff6878e601e32477bc85b53cd0153baf95a161c8935b6e5466c257 1155 tests/audit-service.test.mjs
|
||||
9346a48e2328fc617b2b94caa876a1d60f0098896538224c04b8b4e4c2c023a4 11566 tests/browser/forgeflow.spec.mjs
|
||||
e8802b78b94af7245f7752e0a5f8697064aad2aae8bd002195ae052d69e59670 11619 tests/browser/forgeflow.spec.mjs
|
||||
454edeaccb2bd41043bc918d3e3a6127db14339031d6a1c1562ac855e90455d2 4318 tests/clone-target.test.mjs
|
||||
ac17f8bbe9e388b80abef7792c8b184a1fd482c93f13d23a478e433961020f75 17214 tests/config-store.test.mjs
|
||||
f1463326aee79842d265687ae628189ce54e92544600f2bd14073780287cfb14 2502 tests/configuration-backup.test.mjs
|
||||
@@ -229,7 +230,7 @@ bab853feb0e22aa25af17989baaa632c01efa636533ea67407fecfdd973c7024 627
|
||||
020eccfa9c4aef7a4ac4736d9af90518fcb6d1ad75aedcfaa1c92832a9e3d6d8 4609 tests/shell-verification.test.mjs
|
||||
0d1bc4d623ce299337736c577ec61c8ffd6974ebe20335b72838d10eae35ecb1 7993 tests/ssh-service.test.mjs
|
||||
8a6a8477eb94b85ccef18cddd2640afb0d1eafa679c96bc7de20428d5d69e1be 1794 tests/tool-invocation.test.mjs
|
||||
1c5b40fb0a915ab0448bb9d2394f626c7b12142d91c23393a2d7537b3ea411ab 50853 tests/unraid-deployment.test.mjs
|
||||
7b8e9118c91503c499e3194223f98ab3f689d6071e7f2316be2b700db592e06f 53633 tests/unraid-deployment.test.mjs
|
||||
bf95071f8d8bec6dcf2c889f1f9d83be1b56178dc0d2be84da555848b6319cbc 25650 tests/update-service.test.mjs
|
||||
9cea5c1d5ba3e0972a0b5c7236cf1f7c5616373e0a39ea4a492ecebf70452e40 948 tests/validation.test.mjs
|
||||
7ef4d4b9f5f3e6979293b29d571ce0e39f83197f3cade2d999a9cea7bacdd84d 1781 tests/zip-writer.test.mjs
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# ForgeFlow 0.10.10
|
||||
|
||||
## Complete server-pull deployment repair
|
||||
|
||||
- Missing repository-scoped read-only deploy keys can be provisioned and verified from Unraid against the exact Gitea branch.
|
||||
- A repository deployment root can now remain above its Compose working directory without breaking workload recognition or being overwritten by inventory refresh.
|
||||
- Nested Compose files are preserved as repository-relative deployment paths, including Ludarium, Launchpad and ITWorx MCP Hub layouts.
|
||||
- The **Fix write access** action now receives its permission-report parser correctly instead of reporting a false write-access failure.
|
||||
- Server-pull preflight proves every required deployment file at the exact Gitea commit before any container activation starts.
|
||||
- Runtime secrets remain in server-side `.env` files and preserved appdata paths; no secret values are written to Git or diagnostic output.
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "forgeflow",
|
||||
"version": "0.10.9",
|
||||
"version": "0.10.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "forgeflow",
|
||||
"version": "0.10.9",
|
||||
"version": "0.10.10",
|
||||
"dependencies": {
|
||||
"ssh2": "1.17.0"
|
||||
},
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "forgeflow",
|
||||
"version": "0.10.9",
|
||||
"version": "0.10.10",
|
||||
"private": true,
|
||||
"description": "Desktop release cockpit for local Git, Gitea Actions and controlled exact-commit deployments.",
|
||||
"main": "main.cjs",
|
||||
@@ -114,6 +114,7 @@
|
||||
"docs/RELEASE_NOTES_0.10.7.md",
|
||||
"docs/RELEASE_NOTES_0.10.8.md",
|
||||
"docs/RELEASE_NOTES_0.10.9.md",
|
||||
"docs/RELEASE_NOTES_0.10.10.md",
|
||||
"docs/CURRENT_STATE.md",
|
||||
"docs/MUTATION_MODEL.md",
|
||||
"docs/RELEASING.md",
|
||||
|
||||
+7
-2
@@ -89,6 +89,7 @@ const required = [
|
||||
"docs/RELEASE_NOTES_0.10.7.md",
|
||||
"docs/RELEASE_NOTES_0.10.8.md",
|
||||
"docs/RELEASE_NOTES_0.10.9.md",
|
||||
"docs/RELEASE_NOTES_0.10.10.md",
|
||||
"docs/UPDATING.md",
|
||||
"docs/DIAGNOSTICS.md",
|
||||
"docs/DEPLOYMENT_SETUP.md",
|
||||
@@ -127,9 +128,9 @@ for (const file of required) await access(path.join(root, file));
|
||||
const packageJson = JSON.parse(
|
||||
await readFile(path.join(root, "package.json"), "utf8"),
|
||||
);
|
||||
if (packageJson.version !== "0.10.9")
|
||||
if (packageJson.version !== "0.10.10")
|
||||
throw new Error(
|
||||
`Expected package version 0.10.9, got ${packageJson.version}.`,
|
||||
`Expected package version 0.10.10, got ${packageJson.version}.`,
|
||||
);
|
||||
const sourceManifest = await readFile(
|
||||
path.join(root, "SOURCE_MANIFEST.txt"),
|
||||
@@ -479,6 +480,10 @@ const release0109 = await readFile(path.join(root, "docs/RELEASE_NOTES_0.10.9.md
|
||||
for (const phrase of ["containers without healthchecks", "single-instance", "exact Gitea commit", "deploy-ready", "no containers are changed"]) {
|
||||
if (!release0109.includes(phrase)) throw new Error(`0.10.9 release notes are missing: ${phrase}`);
|
||||
}
|
||||
const release01010 = await readFile(path.join(root, "docs/RELEASE_NOTES_0.10.10.md"), "utf8");
|
||||
for (const phrase of ["read-only deploy keys", "repository deployment root", "Compose working directory", "Fix write access", "exact Gitea commit"]) {
|
||||
if (!release01010.includes(phrase)) throw new Error(`0.10.10 release notes are missing: ${phrase}`);
|
||||
}
|
||||
const configSource = await readFile(path.join(root, "src/main/config-store.cjs"), "utf8");
|
||||
for (const mode of ["server-git", "push-bundle", "monitor-only"]) {
|
||||
if (!configSource.includes(mode)) throw new Error(`Deployment configuration is missing mode: ${mode}`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
function createUnraidAccessMethods({ shellQuote, path, bash, inventoryRemoteIdentity, checksSummary, crypto }) {
|
||||
function createUnraidAccessMethods({ shellQuote, path, bash, inventoryRemoteIdentity, checksSummary, crypto, parsePermissionInspection }) {
|
||||
class UnraidAccessMethods {
|
||||
serverGitRemote(repository, profile) {
|
||||
const candidates = [repository.sshUrl, profile.cloneUrl, repository.preferredCloneUrl]
|
||||
|
||||
@@ -490,7 +490,7 @@ for (const name of Object.getOwnPropertyNames(preflightMethods)) {
|
||||
if (name !== "constructor") Object.defineProperty(UnraidDeploymentService.prototype, name, Object.getOwnPropertyDescriptor(preflightMethods, name));
|
||||
}
|
||||
|
||||
const accessMethods = createUnraidAccessMethods({ shellQuote, path, bash, inventoryRemoteIdentity, checksSummary, crypto });
|
||||
const accessMethods = createUnraidAccessMethods({ shellQuote, path, bash, inventoryRemoteIdentity, checksSummary, crypto, parsePermissionInspection });
|
||||
for (const name of Object.getOwnPropertyNames(accessMethods)) {
|
||||
if (name !== "constructor") Object.defineProperty(UnraidDeploymentService.prototype, name, Object.getOwnPropertyDescriptor(accessMethods, name));
|
||||
}
|
||||
|
||||
@@ -255,19 +255,38 @@ function createUnraidInventoryMethods({
|
||||
}
|
||||
|
||||
refreshedProfileFromWorkload(repository, server, workload, existingProfile) {
|
||||
const configuredRoot = path.join(server.basePath, existingProfile.remoteFolder || "").replace(/\/+$/, "");
|
||||
const composeWorkingDir = String(workload.compose?.workingDir || "").replace(/\/+$/, "");
|
||||
const configuredRootOwnsCompose = Boolean(
|
||||
configuredRoot
|
||||
&& composeWorkingDir
|
||||
&& (composeWorkingDir === configuredRoot || composeWorkingDir.startsWith(`${configuredRoot}/`)),
|
||||
);
|
||||
const detected = this.profileFromWorkload(repository, server, workload, {
|
||||
linkSource: existingProfile.workloadIdentity?.linkSource || "automatic-compose",
|
||||
deploymentMode: ["push-bundle", "server-git", "monitor-only"].includes(existingProfile.deploymentMode)
|
||||
? existingProfile.deploymentMode
|
||||
: "push-bundle",
|
||||
remoteFolder: workload.remoteFolderCandidate || existingProfile.remoteFolder,
|
||||
remoteFolder: configuredRootOwnsCompose
|
||||
? existingProfile.remoteFolder
|
||||
: workload.remoteFolderCandidate || existingProfile.remoteFolder,
|
||||
});
|
||||
const repositoryRelativeComposeFiles = configuredRootOwnsCompose
|
||||
? [...new Set((workload.compose?.configFiles || []).map((file) => {
|
||||
const value = String(file || "").trim().replace(/\\/g, "/");
|
||||
if (value.startsWith(`${configuredRoot}/`)) return value.slice(configuredRoot.length + 1);
|
||||
return value.startsWith("/") ? "" : value;
|
||||
}).filter(Boolean))]
|
||||
: [];
|
||||
const composeFiles = repositoryRelativeComposeFiles.length
|
||||
? repositoryRelativeComposeFiles
|
||||
: detected.composeFiles;
|
||||
return {
|
||||
...existingProfile,
|
||||
deploymentMode: detected.deploymentMode,
|
||||
remoteFolder: detected.remoteFolder,
|
||||
composeFile: detected.composeFile,
|
||||
composeFiles: detected.composeFiles,
|
||||
composeFile: composeFiles[0],
|
||||
composeFiles,
|
||||
composeProject: detected.composeProject,
|
||||
composeWorkingDir: detected.composeWorkingDir,
|
||||
composeService: detected.composeService,
|
||||
|
||||
@@ -5,7 +5,7 @@ function createMockRepositoryBridge(context) {
|
||||
await wait(80);
|
||||
snapshot();
|
||||
return {
|
||||
appVersion: "0.10.9-demo",
|
||||
appVersion: "0.10.10-demo",
|
||||
platform: "win32",
|
||||
state: clone(state),
|
||||
git: { available: true, version: "git version 2.47.3" },
|
||||
|
||||
@@ -74,8 +74,11 @@ async function assertScrollableWhenOverflowing(page, selector) {
|
||||
return metrics.connected && metrics.clientHeight > 0;
|
||||
}).toBe(true);
|
||||
if (metrics.scrollHeight > metrics.clientHeight + 1) {
|
||||
await target.evaluate((element) => { element.scrollTop = element.scrollHeight; });
|
||||
await expect.poll(() => target.evaluate((element) => element.scrollTop)).toBeGreaterThan(0);
|
||||
await expect.poll(() => target.evaluate((element) => {
|
||||
if (element.scrollHeight <= element.clientHeight + 1) return 1;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
return element.scrollTop;
|
||||
})).toBeGreaterThan(0);
|
||||
}
|
||||
}
|
||||
test("shell, overview, repositories and settings remain responsive and accessible", async ({ page }, testInfo) => {
|
||||
|
||||
@@ -40,6 +40,36 @@ test("server release directories resolve to the stable deployment root", () => {
|
||||
assert.equal(deploymentRootCandidate("ludarium/source/deploy"), "ludarium/source/deploy");
|
||||
});
|
||||
|
||||
test("inventory refresh preserves a repository deployment root above its Compose working directory", () => {
|
||||
const service = new UnraidDeploymentService({});
|
||||
const existing = {
|
||||
id: "profile", provider: "ssh-unraid", serverId: "server", environment: "production",
|
||||
branch: "main", deploymentMode: "server-git", remoteFolder: "App/source",
|
||||
composeWorkingDir: "/mnt/user/appdata/App/source/ops", composeProject: "app",
|
||||
composeFiles: ["ops/compose.yml"], composeServices: ["web"], containerName: "app-web",
|
||||
workloadIdentity: { linkSource: "automatic-compose" }, preservePaths: [".env"],
|
||||
};
|
||||
const workload = {
|
||||
workloadId: "workload", kind: "compose-project", displayName: "app",
|
||||
remoteFolderCandidate: "App/source/ops", selector: { kind: "compose-project", composeProject: "app" },
|
||||
compose: {
|
||||
project: "app", workingDir: "/mnt/user/appdata/App/source/ops",
|
||||
configFiles: ["/mnt/user/appdata/App/source/ops/compose.yml"], services: ["web"],
|
||||
},
|
||||
containers: [{ name: "app-web", running: true, service: "web", mounts: [], ports: [] }],
|
||||
metadata: {}, dockerMan: null,
|
||||
};
|
||||
const refreshed = service.refreshedProfileFromWorkload(
|
||||
{ fullName: "Owner/App", name: "App", defaultBranch: "main", sshUrl: "git@gitea.test:Owner/App.git" },
|
||||
{ id: "server", name: "Server", basePath: "/mnt/user/appdata" },
|
||||
workload,
|
||||
existing,
|
||||
);
|
||||
assert.equal(refreshed.remoteFolder, "App/source");
|
||||
assert.equal(refreshed.composeWorkingDir, "/mnt/user/appdata/App/source/ops");
|
||||
assert.deepEqual(refreshed.composeFiles, ["ops/compose.yml"]);
|
||||
});
|
||||
|
||||
test("server inspection key-value payload is decoded safely", () => {
|
||||
const b64 = (value) => Buffer.from(value).toString("base64");
|
||||
const parsed = parseInspection(
|
||||
@@ -86,6 +116,22 @@ test("server pull provisions a pinned repository-scoped key and records access m
|
||||
assert.equal(result.remoteSha, "a".repeat(40));
|
||||
});
|
||||
|
||||
test("write-access inspection parses the remote permission report through the access module", async () => {
|
||||
const encoded = (value) => Buffer.from(value).toString("base64");
|
||||
const profile = { id: "profile", provider: "ssh-unraid", serverId: "server", remoteFolder: "App", composeFiles: ["compose.yml"] };
|
||||
const service = new UnraidDeploymentService({
|
||||
store: {
|
||||
getDeploymentProfile: () => profile,
|
||||
getServer: () => ({ id: "server", basePath: "/mnt/user/appdata" }),
|
||||
},
|
||||
ssh: { exec: async () => ({ stdout: `__FORGEFLOW_PERMISSIONS__\nI\t${encoded("deploy")}\t1000\t1000\t${encoded("users")}\tfalse\tfalse\nP\t${encoded("project-root")}\t${encoded("Project folder")}\t${encoded("/mnt/user/appdata/App")}\tdirectory\ttrue\ttrue\ttrue\ttrue\ttrue\ttrue\t${encoded("deploy")}\t${encoded("users")}\t2775\t${encoded("/mnt/user/appdata/App")}\t${encoded("Read/write probe passed.")}\n` }) },
|
||||
});
|
||||
const report = await service.inspectWriteAccess({ repository: { fullName: "Owner/App" }, profileId: "profile" });
|
||||
assert.equal(report.ready, true);
|
||||
assert.equal(report.identity.user, "deploy");
|
||||
assert.equal(report.targets[0].effectiveWritable, true);
|
||||
});
|
||||
|
||||
test("server pull verification proves a repository-scoped read-only key and exact commit parity", async () => {
|
||||
const sha = "c".repeat(40);
|
||||
const profile = {
|
||||
|
||||
Reference in New Issue
Block a user