diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 17443cd..0000000 --- a/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -worker/node_modules -worker/allure-* -worker/lib \ No newline at end of file diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index a05d10c..59fbb60 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -24,6 +24,15 @@ jobs: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up Nodejs + uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Install dependencies + run: | + cd ./worker + npm install && npm run build + - name: Build and push uses: docker/build-push-action@v6.10.0 with: diff --git a/Dockerfile b/Dockerfile index 0805a38..de8fc53 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,16 +2,12 @@ FROM node:22-alpine AS deps USER root RUN apk add openjdk17-jre RUN npm install -g firebase-tools -RUN npm install -g typescript FROM deps AS prod WORKDIR /app -COPY worker/. /app/ -RUN npm install --omit=dev && npm run build -RUN npm uninstall -g typescript -## Remove .ts files -RUN rm -rf app +COPY worker/lib ./lib +COPY worker/node_modules ./node_modules COPY start.sh / CMD ["/bin/sh", "/start.sh"] \ No newline at end of file diff --git a/README.md b/README.md index 652fc8f..8b0a6be 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ jobs: run: | docker run --rm \ -e STORAGE_BUCKET=my-test-results-bucket \ # Specify your Firebase Storage bucket + -e PREFIX=project-123 \ # A path in your storage bucket (Optional) -e WEBSITE_ID=my-custom-site-id \ # Use a unique ID for the report website -e WEBSITE_EXPIRES=3d \ # Set the report expiration (e.g., 3 days) -e KEEP_HISTORY=true \ # Retain historical test data @@ -154,6 +155,7 @@ services: WEBSITE_ID: your-site-id # Assign an ID to your Allure report website WEBSITE_EXPIRES: 2d # Duration of availability. 1-30 days STORAGE_BUCKET: your-storage-bucket # Google Cloud storage bucket + PREFIX: project-123 # A path in your storage bucket (Optional) KEEP_HISTORY: true # Default is true when STORAGE_BUCKET is provided KEEP_RESULTS: false # Default is false WATCH_MODE: true @@ -169,6 +171,7 @@ ___ | Variable | Description | Example | Default | |--------------------|-------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|---------| | `STORAGE_BUCKET` | Google Cloud Storage bucket name | project-id.firebasestorage.app | None | +| `PREFIX` | A path in your Storage bucket. Optional. | project-123 | None | | `WEBSITE_ID` | Unique identifier for hosted reports | test-report-id | None | | `WEBSITE_EXPIRES` | Expiration duration for reports. Examples: 1h, 2d, 3w | 29d | 7d | | `KEEP_HISTORY` | Backup `reports/history` directory after report generation. | true | true | diff --git a/assets/docker-logo.png b/assets/docker-logo.png deleted file mode 100644 index 600476c..0000000 Binary files a/assets/docker-logo.png and /dev/null differ diff --git a/assets/firebase-logo.png b/assets/firebase-logo.png deleted file mode 100644 index 4cfb7da..0000000 Binary files a/assets/firebase-logo.png and /dev/null differ diff --git a/docker-compose.yaml b/docker-compose.yaml index bf5438e..8a1ab90 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -5,6 +5,7 @@ services: ports: - "9199:9199" - "4000:4000" + - "5088:5088" command: ["firebase", "emulators:start", "--only", "storage,logging", "--project", "fir-demo-project", '--config', "./startfirebase.json"] restart: unless-stopped healthcheck: @@ -17,11 +18,21 @@ services: build: dockerfile: Dockerfile context: . + develop: + watch: + - action: sync+restart + path: ./worker/lib + target: /app/lib + ignore: + - node_modules/ + - action: rebuild + path: package.json container_name: allure-deployer env_file: .env.slack environment: FIREBASE_STORAGE_EMULATOR_HOST: 127.0.0.1:9199 # Dev mode STORAGE_BUCKET: "fir-demo-project.appspot.com" + PREFIX: 'project-999' WEBSITE_ID: 'fir-demo-project' WEBSITE_EXPIRES: 12h KEEP_HISTORY: true # Default is true when STORAGE_BUCKET is provided @@ -29,7 +40,7 @@ services: WATCH_MODE: false TTL_SECS: 6 volumes: -# - ./worker:/app #Devs only. This overrides the /app dir +# - ./worker:/app #Dev only. This overrides the /app dir - ${PWD}/gcp-key.json:/credentials/key.json:ro - /Users/sokari/IdeaProjects/appium/reports/ios/allure-results:/allure-results cpus: 1 diff --git a/worker/app/cloud-storage.ts b/worker/app/cloud-storage.ts index 8d67ab4..c906cf8 100644 --- a/worker/app/cloud-storage.ts +++ b/worker/app/cloud-storage.ts @@ -6,7 +6,8 @@ import {getAllFilesStream} from "./util"; import counter from "./counter"; import pLimit from "p-limit"; -const storageHomeDir = 'allure-results' +const prefix = process.env.PREFIX || undefined +const storageHomeDir = prefix ? `${prefix}/allure-results` : 'allure-results' /** * CloudStorage Class * diff --git a/worker/app/counter.ts b/worker/app/counter.ts index 6c4d84f..532420a 100644 --- a/worker/app/counter.ts +++ b/worker/app/counter.ts @@ -31,7 +31,9 @@ class Counter { get filesProcessed(){return this.processed} startTimer(): void { - this.startTime = Date.now(); + if(!this.startTime){ + this.startTime = Date.now(); + } } getElapsedSeconds(): string { if (!this.startTime) { diff --git a/worker/app/notifier.ts b/worker/app/notifier.ts index a9e1811..6639388 100644 --- a/worker/app/notifier.ts +++ b/worker/app/notifier.ts @@ -7,6 +7,7 @@ import ansiEscapes from "ansi-escapes"; import chalk from "chalk"; import credential from "./credential"; import {appLog} from "./util"; + type SlackCredentials = { token: string, conversationId: string, @@ -22,6 +23,9 @@ type SlackCredentials = { export class Notifier { private get dashboardUrl() { + if (DEBUG) { + return `http://127.0.0.1:4000/storage/${STORAGE_BUCKET}` + } return new StringBuilder().append("https://console.firebase.google.com/project") .append(`/${(credential.projectId)}`) .append(`/storage/${STORAGE_BUCKET}/files`) @@ -178,8 +182,6 @@ export class Notifier { if (cloudStorage) { if (keepHistory && keepResults) { appLog(`KEEP_HISTORY and KEEP_RESULTS enabled`) - } else if (!keepHistory && !keepResults) { - appLog(`KEEP_HISTORY and KEEP_RESULTS disabled`) } else if (keepHistory) { appLog(`KEEP_HISTORY enabled`) } else if (keepResults) { diff --git a/worker/app/report-builder.ts b/worker/app/report-builder.ts index 1f9e32b..b334ef1 100644 --- a/worker/app/report-builder.ts +++ b/worker/app/report-builder.ts @@ -115,7 +115,6 @@ class ReportBuilder { ) } await Promise.all(tasks) - return this } } diff --git a/worker/app/util.ts b/worker/app/util.ts index 0bbcba7..b6ad33a 100644 --- a/worker/app/util.ts +++ b/worker/app/util.ts @@ -4,7 +4,7 @@ import * as path from "node:path"; import util from "node:util"; const exec = util.promisify(require('child_process').exec) -import { websiteId} from "./constant"; +import {DEBUG, websiteId} from "./constant"; import {StringBuilder} from "./string-builder"; import credential from "./credential"; @@ -92,10 +92,6 @@ export async function changePermissionsRecursively(dirPath: string, mode: fsSync * @returns {Promise} - The URL of the deployed site, if successful */ export async function publishToFireBaseHosting(configParentDir: string): Promise { - // if (DEBUG) { - // console.warn('DEBUG=true: Skipping live deployment') - // return null - // } const hosting = { "hosting": { "public": ".", @@ -136,6 +132,10 @@ export async function publishToFireBaseHosting(configParentDir: string): Promise builder.append('7d') } + if (DEBUG) { + appLog('DEBUG mode, skipping live deployment') + return 'http://127.0.0.1:8080/'; + } const {stdout, stderr} = await exec(builder.toString()) if (stderr && !stdout) { @@ -157,18 +157,6 @@ export async function publishToFireBaseHosting(configParentDir: string): Promise } -// async function isSiteAvailable(siteId: string) { -// const builder = new StringBuilder() -// builder.append('firebase hosting:sites:get') -// .append(' ').append(siteId) -// .append(' ').append('--project') -// .append(' ').append(credential.projectId) -// const {stdout, stderr} = await exec(builder.toString()) -// if(stderr && !stdout) return false -// -// return stdout.contains(siteId) -// } - diff --git a/worker/index.ts b/worker/index.ts index d6b1ad4..21536e4 100644 --- a/worker/index.ts +++ b/worker/index.ts @@ -77,7 +77,7 @@ export function main(): void { keepResults && cloudStorage ? cloudStorage.uploadResults() : null ]))[0] - // Print GitHub summary + // Prepare GitHub summary const githubStepSummary = process.env.GITHUB_STEP_SUMMARY const notifier = new Notifier() const notifierPromises : Promise[] = [] @@ -89,7 +89,7 @@ export function main(): void { } else { notifier.printSummaryToConsole({url: url}) } - // Send Slack message + // Prepare Slack message const token = process.env.SLACK_TOKEN; const conversationId = process.env.SLACK_CHANNEL_ID; if (conversationId && token) { @@ -98,6 +98,7 @@ export function main(): void { token: token, url: url })) } + // Send notifications async if(notifierPromises.length > 0){ await Promise.all(notifierPromises) } diff --git a/worker/package-lock.json b/worker/package-lock.json index bb65ef0..a5d68cd 100644 --- a/worker/package-lock.json +++ b/worker/package-lock.json @@ -1817,13 +1817,10 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -3062,6 +3059,17 @@ "devOptional": true, "license": "MIT" }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -4514,6 +4522,20 @@ "node": ">= 14" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -6824,7 +6846,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/seed-random": { diff --git a/worker/package.json b/worker/package.json index 1e52033..f34a5e2 100644 --- a/worker/package.json +++ b/worker/package.json @@ -32,9 +32,5 @@ "ts-node-dev": "^2.0.0", "typescript": "^4.9.0" }, - "overrides": { - "ajv": "^8.17.1", - "whatwg-url": "^14.0.0" - }, "private": true }