@i-santos/firestack 1.0.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/CHANGELOG.md +10 -0
- package/README.md +225 -0
- package/bin/firestack.mjs +122 -0
- package/package.json +36 -0
- package/scripts/cli/config-migrate.mjs +129 -0
- package/scripts/cli/config.mjs +14 -0
- package/scripts/cli/docker-init.mjs +102 -0
- package/scripts/cli/docker-runner.mjs +579 -0
- package/scripts/cli/env.mjs +168 -0
- package/scripts/cli/functions-env.mjs +98 -0
- package/scripts/cli/init.mjs +134 -0
- package/scripts/cli/install.mjs +105 -0
- package/scripts/cli/internal-e2e-runner.mjs +94 -0
- package/scripts/cli/internal-log-router.mjs +116 -0
- package/scripts/cli/internal-run-e2e-staging.mjs +49 -0
- package/scripts/cli/internal-run-e2e.mjs +153 -0
- package/scripts/cli/internal-run-functions-build.mjs +91 -0
- package/scripts/cli/internal-run-integration-report.mjs +132 -0
- package/scripts/cli/test.mjs +1094 -0
- package/scripts/publish-package.sh +16 -0
- package/templates/dockerignore +37 -0
- package/templates/env/.env.default.example +4 -0
- package/templates/env/.env.development.example +4 -0
- package/templates/env/.env.production.example +2 -0
- package/templates/env/.env.staging.example +4 -0
- package/templates/env/.env.test.default.example +6 -0
- package/templates/env/.env.test.development.example +6 -0
- package/templates/env/.env.test.production.example +4 -0
- package/templates/env/.env.test.staging.example +8 -0
- package/templates/firestack.config.json +101 -0
- package/templates/playwright.config.mjs +65 -0
- package/templates/tests.Dockerfile +43 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
REGISTRY="${FIRESTACK_REGISTRY:-http://127.0.0.1:4873}"
|
|
6
|
+
NPM_CACHE="${FIRESTACK_NPM_CACHE:-/tmp/npm-cache-firestack}"
|
|
7
|
+
|
|
8
|
+
cd "$ROOT_DIR"
|
|
9
|
+
|
|
10
|
+
if ! npm --cache "$NPM_CACHE" whoami --registry "$REGISTRY" >/dev/null 2>&1; then
|
|
11
|
+
echo "[firestack] npm auth missing for $REGISTRY"
|
|
12
|
+
echo "[firestack] run: npm adduser --registry $REGISTRY"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
npm --cache "$NPM_CACHE" publish --registry "$REGISTRY"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Firestack default Docker build context ignore list.
|
|
2
|
+
# Keep package manifests and lockfiles available for deterministic installs.
|
|
3
|
+
|
|
4
|
+
.git
|
|
5
|
+
.github
|
|
6
|
+
.idea
|
|
7
|
+
.vscode
|
|
8
|
+
|
|
9
|
+
out
|
|
10
|
+
coverage
|
|
11
|
+
dist
|
|
12
|
+
build
|
|
13
|
+
tmp
|
|
14
|
+
temp
|
|
15
|
+
|
|
16
|
+
.cache
|
|
17
|
+
.parcel-cache
|
|
18
|
+
.next
|
|
19
|
+
.nuxt
|
|
20
|
+
.svelte-kit
|
|
21
|
+
.turbo
|
|
22
|
+
.nx
|
|
23
|
+
|
|
24
|
+
.firebase
|
|
25
|
+
firebase-debug.log*
|
|
26
|
+
|
|
27
|
+
node_modules
|
|
28
|
+
**/node_modules
|
|
29
|
+
playwright-report
|
|
30
|
+
test-results
|
|
31
|
+
|
|
32
|
+
*.tsbuildinfo
|
|
33
|
+
.DS_Store
|
|
34
|
+
npm-debug.log*
|
|
35
|
+
yarn-debug.log*
|
|
36
|
+
yarn-error.log*
|
|
37
|
+
pnpm-debug.log*
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Staging test environment
|
|
2
|
+
GCLOUD_PROJECT=staging-your-project
|
|
3
|
+
E2E_BASE_URL=https://staging.example.com
|
|
4
|
+
ALLOW_NON_STAGING_E2E=false
|
|
5
|
+
E2E_STAGING_SMOKE_EMAIL=e2e-smoke@example.com
|
|
6
|
+
E2E_STAGING_SMOKE_PASSWORD=change-me
|
|
7
|
+
E2E_STAGING_FULL_EMAIL=e2e-full@example.com
|
|
8
|
+
E2E_STAGING_FULL_PASSWORD=change-me
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"env": {
|
|
4
|
+
"profiles": {
|
|
5
|
+
"default": {
|
|
6
|
+
"files": [
|
|
7
|
+
{
|
|
8
|
+
"target": ".env.default",
|
|
9
|
+
"template": "env/.env.default.example"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"target": ".env.test.default",
|
|
13
|
+
"template": "env/.env.test.default.example"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"staging": {
|
|
18
|
+
"files": [
|
|
19
|
+
{
|
|
20
|
+
"target": ".env.staging",
|
|
21
|
+
"template": "env/.env.staging.example"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"target": ".env.test.staging",
|
|
25
|
+
"template": "env/.env.test.staging.example"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"production": {
|
|
30
|
+
"files": [
|
|
31
|
+
{
|
|
32
|
+
"target": ".env.production",
|
|
33
|
+
"template": "env/.env.production.example"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"target": ".env.test.production",
|
|
37
|
+
"template": "env/.env.test.production.example"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"test": {
|
|
44
|
+
"commands": {
|
|
45
|
+
"unit": "mkdir -p out/tests/unit && node --test --experimental-strip-types --test-reporter=spec --test-reporter=junit --test-reporter-destination=stdout --test-reporter-destination=out/tests/unit/junit.xml tests/unit/**/*.test.ts",
|
|
46
|
+
"integration": "firestack internal run-functions-build && firebase emulators:exec --project ${GCLOUD_PROJECT:?Set GCLOUD_PROJECT} \"firestack internal run-integration-report\"",
|
|
47
|
+
"e2eSmoke": "mkdir -p out/tests/e2e && rm -f out/tests/e2e/junit.xml && rm -rf out/tests/e2e/html out/tests/e2e/artifacts && firestack internal run-e2e smoke",
|
|
48
|
+
"e2eFull": "mkdir -p out/tests/e2e && rm -f out/tests/e2e/junit.xml && rm -rf out/tests/e2e/html out/tests/e2e/artifacts && firestack internal run-e2e full",
|
|
49
|
+
"stagingSmoke": "mkdir -p out/tests/e2e/staging && rm -f out/tests/e2e/staging/junit.xml && rm -rf out/tests/e2e/staging/html out/tests/e2e/staging/artifacts && firestack internal run-e2e-staging smoke",
|
|
50
|
+
"stagingFull": "mkdir -p out/tests/e2e/staging && rm -f out/tests/e2e/staging/junit.xml && rm -rf out/tests/e2e/staging/html out/tests/e2e/staging/artifacts && firestack internal run-e2e-staging full",
|
|
51
|
+
"ci": "mkdir -p out/tests/unit out/tests/e2e && rm -rf out/tests/e2e/html out/tests/e2e/artifacts && node --test --experimental-strip-types --test-reporter=spec --test-reporter=junit --test-reporter-destination=stdout --test-reporter-destination=out/tests/unit/junit.xml tests/unit/**/*.test.ts && firestack internal run-functions-build && firebase emulators:exec --project ${GCLOUD_PROJECT:?Set GCLOUD_PROJECT} \"bash -lc 'set +e; firestack internal run-integration-report; integration_status=\\$?; firestack internal run-e2e smoke; e2e_status=\\$?; set -e; if [ \\$integration_status -ne 0 ] || [ \\$e2e_status -ne 0 ]; then exit 1; fi'\"",
|
|
52
|
+
"ciFailFast": "mkdir -p out/tests/unit out/tests/e2e && rm -rf out/tests/e2e/html out/tests/e2e/artifacts && node --test --experimental-strip-types --test-reporter=spec --test-reporter=junit --test-reporter-destination=stdout --test-reporter-destination=out/tests/unit/junit.xml tests/unit/**/*.test.ts && firestack internal run-functions-build && firebase emulators:exec --project ${GCLOUD_PROJECT:?Set GCLOUD_PROJECT} \"firestack internal run-integration-report && firestack internal run-e2e smoke\""
|
|
53
|
+
},
|
|
54
|
+
"docker": {
|
|
55
|
+
"dockerfile": "tests/Dockerfile",
|
|
56
|
+
"imageBaseName": "firestack-tests",
|
|
57
|
+
"nodeModulesVolumePrefix": "firestack-node_modules-",
|
|
58
|
+
"functionsNodeModulesVolumePrefix": "firestack-functions-node_modules-",
|
|
59
|
+
"emulatorCacheVolumePrefix": "firestack-firebase-cache-",
|
|
60
|
+
"buildNetwork": "host",
|
|
61
|
+
"workdir": "/work",
|
|
62
|
+
"runAsHostUser": true,
|
|
63
|
+
"preloadFirestoreEmulator": true,
|
|
64
|
+
"writablePaths": [
|
|
65
|
+
"out"
|
|
66
|
+
],
|
|
67
|
+
"bootstrapCommand": "if [ ! -d /work/node_modules/.bin ]; then mkdir -p /work/node_modules && cp -a /opt/deps/node_modules/. /work/node_modules/; fi && if [ -n \"${FIRESTACK_FUNCTIONS_PATHS:-}\" ]; then IFS=',' read -r -a firestack_functions <<< \"$FIRESTACK_FUNCTIONS_PATHS\"; for rel in \"${firestack_functions[@]}\"; do if [ -n \"$rel\" ] && [ -d \"/opt/deps/$rel/node_modules\" ] && [ -f \"/work/$rel/package.json\" ] && [ ! -d \"/work/$rel/node_modules/.bin\" ]; then mkdir -p \"/work/$rel/node_modules\" && cp -a \"/opt/deps/$rel/node_modules/.\" \"/work/$rel/node_modules/\"; fi; done; fi",
|
|
68
|
+
"cleanup": true,
|
|
69
|
+
"addHosts": [
|
|
70
|
+
"host.docker.internal:host-gateway"
|
|
71
|
+
],
|
|
72
|
+
"passThroughEnv": [
|
|
73
|
+
"GCLOUD_PROJECT",
|
|
74
|
+
"FIREBASE_PROJECT_ALIAS",
|
|
75
|
+
"ALLOW_NON_STAGING_E2E",
|
|
76
|
+
"E2E_BASE_URL",
|
|
77
|
+
"E2E_CLEANUP",
|
|
78
|
+
"E2E_RUN_ID",
|
|
79
|
+
"E2E_EMAIL",
|
|
80
|
+
"E2E_PASSWORD",
|
|
81
|
+
"E2E_STAGING_EMAIL",
|
|
82
|
+
"E2E_STAGING_PASSWORD",
|
|
83
|
+
"E2E_STAGING_SMOKE_EMAIL",
|
|
84
|
+
"E2E_STAGING_SMOKE_PASSWORD",
|
|
85
|
+
"E2E_STAGING_FULL_EMAIL",
|
|
86
|
+
"E2E_STAGING_FULL_PASSWORD",
|
|
87
|
+
"E2E_APP_CHECK_DEBUG_TOKEN",
|
|
88
|
+
"FIREBASE_AUTH_EMULATOR_HOST",
|
|
89
|
+
"FIRESTORE_EMULATOR_HOST",
|
|
90
|
+
"VITE_USE_FIREBASE_EMULATOR",
|
|
91
|
+
"VITE_FIREBASE_EMULATOR_HOST",
|
|
92
|
+
"STRIPE_WEBHOOK_SECRET",
|
|
93
|
+
"STRIPE_SECRET_KEY"
|
|
94
|
+
],
|
|
95
|
+
"registry": {
|
|
96
|
+
"defaultHostUrl": "http://127.0.0.1:4873",
|
|
97
|
+
"defaultDockerUrl": "http://host.docker.internal:4873"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
const baseURL = process.env.E2E_BASE_URL ?? 'http://127.0.0.1:5173';
|
|
4
|
+
|
|
5
|
+
function normalizeHost(rawUrl) {
|
|
6
|
+
try {
|
|
7
|
+
return new URL(rawUrl).hostname.toLowerCase();
|
|
8
|
+
} catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function resolvePlaywrightOutputBaseDir(env = process.env) {
|
|
14
|
+
const explicitJunit = env.PLAYWRIGHT_JUNIT_OUTPUT_FILE?.trim();
|
|
15
|
+
const explicitHtml = env.PLAYWRIGHT_HTML_OUTPUT_DIR?.trim();
|
|
16
|
+
const explicitArtifacts = env.PLAYWRIGHT_OUTPUT_DIR?.trim();
|
|
17
|
+
if (explicitJunit || explicitHtml || explicitArtifacts) {
|
|
18
|
+
return 'out/tests/e2e';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const profileAlias = env.FIREBASE_PROJECT_ALIAS?.trim().toLowerCase();
|
|
22
|
+
if (profileAlias === 'staging') {
|
|
23
|
+
return 'out/tests/e2e/staging';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const host = normalizeHost(env.E2E_BASE_URL?.trim());
|
|
27
|
+
if (host === 'staging.presentgoal.com') {
|
|
28
|
+
return 'out/tests/e2e/staging';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return 'out/tests/e2e';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const baseOutputDir = resolvePlaywrightOutputBaseDir(process.env);
|
|
35
|
+
const junitOutputFile = process.env.PLAYWRIGHT_JUNIT_OUTPUT_FILE ?? `${baseOutputDir}/junit.xml`;
|
|
36
|
+
const htmlOutputFolder = process.env.PLAYWRIGHT_HTML_OUTPUT_DIR ?? `${baseOutputDir}/html`;
|
|
37
|
+
const outputDir = process.env.PLAYWRIGHT_OUTPUT_DIR ?? `${baseOutputDir}/artifacts`;
|
|
38
|
+
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
testDir: 'tests/e2e',
|
|
41
|
+
outputDir,
|
|
42
|
+
timeout: 30_000,
|
|
43
|
+
expect: {
|
|
44
|
+
timeout: 5_000,
|
|
45
|
+
},
|
|
46
|
+
fullyParallel: true,
|
|
47
|
+
retries: process.env.CI ? 1 : 0,
|
|
48
|
+
reporter: [
|
|
49
|
+
['list'],
|
|
50
|
+
['html', { outputFolder: htmlOutputFolder, open: 'never' }],
|
|
51
|
+
['junit', { outputFile: junitOutputFile }],
|
|
52
|
+
],
|
|
53
|
+
use: {
|
|
54
|
+
baseURL,
|
|
55
|
+
trace: 'retain-on-failure',
|
|
56
|
+
video: 'retain-on-failure',
|
|
57
|
+
screenshot: 'only-on-failure',
|
|
58
|
+
},
|
|
59
|
+
projects: [
|
|
60
|
+
{
|
|
61
|
+
name: 'chromium',
|
|
62
|
+
use: { ...devices['Desktop Chrome'] },
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
FROM mcr.microsoft.com/playwright:v1.58.2-noble AS base
|
|
2
|
+
FROM eclipse-temurin:21-jre AS jre21
|
|
3
|
+
FROM base
|
|
4
|
+
|
|
5
|
+
USER root
|
|
6
|
+
|
|
7
|
+
ARG FIREBASE_CONFIG_PATH=firebase.json
|
|
8
|
+
ARG FIRESTACK_FUNCTIONS_INSTALL_PATHS=
|
|
9
|
+
|
|
10
|
+
COPY --from=jre21 /opt/java/openjdk /opt/java/openjdk
|
|
11
|
+
ENV JAVA_HOME=/opt/java/openjdk
|
|
12
|
+
ENV PATH="${JAVA_HOME}/bin:${PATH}"
|
|
13
|
+
|
|
14
|
+
WORKDIR /opt/deps
|
|
15
|
+
|
|
16
|
+
COPY package*.json ./
|
|
17
|
+
|
|
18
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
19
|
+
if [ -f package.json ]; then (npm ci || npm install); fi
|
|
20
|
+
|
|
21
|
+
RUN npm install -g firebase-tools \
|
|
22
|
+
&& mkdir -p /opt/firebase/emulators \
|
|
23
|
+
&& FIREBASE_EMULATORS_PATH=/opt/firebase/emulators firebase setup:emulators:firestore
|
|
24
|
+
|
|
25
|
+
COPY . .
|
|
26
|
+
|
|
27
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
28
|
+
if [ -n "$FIRESTACK_FUNCTIONS_INSTALL_PATHS" ]; then \
|
|
29
|
+
printf '%s' "$FIRESTACK_FUNCTIONS_INSTALL_PATHS" | tr ',' '\n' > /tmp/firestack-functions-paths; \
|
|
30
|
+
while IFS= read -r rel || [ -n "$rel" ]; do \
|
|
31
|
+
[ -z "$rel" ] && continue; \
|
|
32
|
+
[ ! -f "$rel/package.json" ] && continue; \
|
|
33
|
+
(cd "$rel" && (npm ci || npm install)); \
|
|
34
|
+
done < /tmp/firestack-functions-paths; \
|
|
35
|
+
elif [ -f "$FIREBASE_CONFIG_PATH" ]; then \
|
|
36
|
+
FIREBASE_CONFIG_PATH="$FIREBASE_CONFIG_PATH" node -e "const fs=require('fs');const p=process.env.FIREBASE_CONFIG_PATH||'firebase.json';const cfg=JSON.parse(fs.readFileSync(p,'utf8'));const found=[];const add=(v)=>{if(typeof v!=='string')return;const n=v.trim().replace(/\\\\/g,'/').replace(/^\\.\\//,'');if(!n||n.startsWith('/')||n.includes('..'))return;if(fs.existsSync(n+'/package.json'))found.push(n);};const f=cfg.functions;if(typeof f==='string')add(f);else if(Array.isArray(f)){for(const e of f){if(typeof e==='string')add(e);else if(e&&typeof e==='object')add(e.source);}}else if(f&&typeof f==='object')add(f.source);process.stdout.write([...new Set(found)].join('\n'));" >/tmp/firestack-functions-paths; \
|
|
37
|
+
while IFS= read -r rel || [ -n "$rel" ]; do \
|
|
38
|
+
[ -z "$rel" ] && continue; \
|
|
39
|
+
(cd "$rel" && (npm ci || npm install)); \
|
|
40
|
+
done < /tmp/firestack-functions-paths; \
|
|
41
|
+
elif [ -f functions/package.json ]; then (cd functions && (npm ci || npm install)); fi
|
|
42
|
+
|
|
43
|
+
WORKDIR /work
|