Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
009703f7fb | ||
|
|
1ab428b949 | ||
|
|
97bdc4692c |
@@ -87,6 +87,33 @@ export function mergeEnvironment(source, overrides = {}) {
|
|||||||
return rows.map((row) => row.raw ?? `${row.key}=${row.value}`).join("\n");
|
return rows.map((row) => row.raw ?? `${row.key}=${row.value}`).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveSecretEnvironment(secretEnvironment = {}, environment = process.env) {
|
||||||
|
if (
|
||||||
|
secretEnvironment === null ||
|
||||||
|
typeof secretEnvironment !== "object" ||
|
||||||
|
Array.isArray(secretEnvironment)
|
||||||
|
) {
|
||||||
|
throw new Error("secretEnvironment must be an object.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolved = {};
|
||||||
|
for (const [runtimeKey, rawSecretName] of Object.entries(secretEnvironment)) {
|
||||||
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(runtimeKey)) {
|
||||||
|
throw new Error(`Invalid secret environment key ${runtimeKey}.`);
|
||||||
|
}
|
||||||
|
const secretName = String(rawSecretName);
|
||||||
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(secretName)) {
|
||||||
|
throw new Error(`Invalid Actions secret name for ${runtimeKey}.`);
|
||||||
|
}
|
||||||
|
const value = environment[secretName];
|
||||||
|
if (value === undefined || String(value).length === 0) {
|
||||||
|
throw new Error(`Missing Actions secret ${secretName} for runtime environment ${runtimeKey}.`);
|
||||||
|
}
|
||||||
|
resolved[runtimeKey] = String(value);
|
||||||
|
}
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
async function readBody(response) {
|
async function readBody(response) {
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
if (!text) return null;
|
if (!text) return null;
|
||||||
@@ -605,9 +632,12 @@ function promoteArtifactAliases(artifacts, releaseTag, options) {
|
|||||||
async function deployApplications({ applications, artifacts, sha, client }) {
|
async function deployApplications({ applications, artifacts, sha, client }) {
|
||||||
for (const application of applications) {
|
for (const application of applications) {
|
||||||
const artifact = artifacts.get(application.artifact);
|
const artifact = artifacts.get(application.artifact);
|
||||||
const environment = Object.fromEntries(
|
const environment = {
|
||||||
Object.entries(application.environment ?? {}).map(([key, value]) => [key, expandTokens(value, { sha })]),
|
...Object.fromEntries(
|
||||||
);
|
Object.entries(application.environment ?? {}).map(([key, value]) => [key, expandTokens(value, { sha })]),
|
||||||
|
),
|
||||||
|
...resolveSecretEnvironment(application.secretEnvironment),
|
||||||
|
};
|
||||||
const result = await deployExactImage({
|
const result = await deployExactImage({
|
||||||
client,
|
client,
|
||||||
appId: application.appId,
|
appId: application.appId,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
deployExactImage,
|
deployExactImage,
|
||||||
expandTokens,
|
expandTokens,
|
||||||
mergeEnvironment,
|
mergeEnvironment,
|
||||||
|
resolveSecretEnvironment,
|
||||||
orderApplications,
|
orderApplications,
|
||||||
resolveDeploymentBranch,
|
resolveDeploymentBranch,
|
||||||
resolvePullRequestHeadBranch,
|
resolvePullRequestHeadBranch,
|
||||||
@@ -170,6 +171,24 @@ test("preserves response-only fields and existing environment secrets safely", (
|
|||||||
assert.equal(mergeEnvironment(app().envVars, { ENVIRONMENT: "new" }), "SECRET=preserved\nENVIRONMENT=new");
|
assert.equal(mergeEnvironment(app().envVars, { ENVIRONMENT: "new" }), "SECRET=preserved\nENVIRONMENT=new");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("maps only explicitly declared Actions secrets into runtime environment", () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
resolveSecretEnvironment(
|
||||||
|
{ MINIO_ENDPOINT: "MINIO_ENDPOINT", MINIO_REGION: "MINIO_REGION" },
|
||||||
|
{ MINIO_ENDPOINT: "https://minio.example.test", MINIO_REGION: "us-east-1" },
|
||||||
|
),
|
||||||
|
{ MINIO_ENDPOINT: "https://minio.example.test", MINIO_REGION: "us-east-1" },
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => resolveSecretEnvironment({ MINIO_ENDPOINT: "MINIO_ENDPOINT" }, {}),
|
||||||
|
/Missing Actions secret MINIO_ENDPOINT/,
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => resolveSecretEnvironment({ "INVALID-KEY": "MINIO_ENDPOINT" }, { MINIO_ENDPOINT: "value" }),
|
||||||
|
/Invalid secret environment key/,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("postflight waits for the exact expected identity", async () => {
|
test("postflight waits for the exact expected identity", async () => {
|
||||||
let attempt = 0;
|
let attempt = 0;
|
||||||
const result = await verifyEndpoint(
|
const result = await verifyEndpoint(
|
||||||
|
|||||||
@@ -34,3 +34,10 @@ jobs:
|
|||||||
QUICKSTACK_API_TOKEN: ${{ secrets.QUICKSTACK_API_TOKEN }}
|
QUICKSTACK_API_TOKEN: ${{ secrets.QUICKSTACK_API_TOKEN }}
|
||||||
QUICKSTACK_BASE_URL: https://server.nuvisphere.de
|
QUICKSTACK_BASE_URL: https://server.nuvisphere.de
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
MINIO_ENDPOINT: ${{ secrets.MINIO_ENDPOINT }}
|
||||||
|
NEXT_PUBLIC_MINIO_ENDPOINT: ${{ secrets.NEXT_PUBLIC_MINIO_ENDPOINT }}
|
||||||
|
MINIO_REGION: ${{ secrets.MINIO_REGION }}
|
||||||
|
MINIO_ROOT_USER: ${{ secrets.MINIO_ROOT_USER }}
|
||||||
|
MINIO_ROOT_PASSWORD: ${{ secrets.MINIO_ROOT_PASSWORD }}
|
||||||
|
MINIO_TLS_REJECT_UNAUTHORIZED: ${{ secrets.MINIO_TLS_REJECT_UNAUTHORIZED }}
|
||||||
|
MINIO_AVATAR_BUCKET: ${{ secrets.MINIO_AVATAR_BUCKET }}
|
||||||
|
|||||||
Reference in New Issue
Block a user