allure-report-publisher 5.0.0-alpha.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/LICENSE.txt +21 -0
- package/README.md +253 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +5 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/commands/upload/index.d.ts +31 -0
- package/dist/commands/upload/index.js +198 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/allure/config.d.ts +8 -0
- package/dist/lib/allure/config.js +125 -0
- package/dist/lib/allure/report-generator.d.ts +11 -0
- package/dist/lib/allure/report-generator.js +43 -0
- package/dist/lib/ci/info/base.d.ts +6 -0
- package/dist/lib/ci/info/base.js +4 -0
- package/dist/lib/ci/info/github.d.ts +9 -0
- package/dist/lib/ci/info/github.js +21 -0
- package/dist/lib/ci/info/gitlab.d.ts +17 -0
- package/dist/lib/ci/info/gitlab.js +47 -0
- package/dist/lib/uploader/base.d.ts +40 -0
- package/dist/lib/uploader/base.js +101 -0
- package/dist/lib/uploader/s3.d.ts +17 -0
- package/dist/lib/uploader/s3.js +113 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/ci.d.ts +5 -0
- package/dist/utils/ci.js +13 -0
- package/dist/utils/config.d.ts +17 -0
- package/dist/utils/config.js +43 -0
- package/dist/utils/glob.d.ts +4 -0
- package/dist/utils/glob.js +35 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.js +51 -0
- package/dist/utils/spinner.d.ts +4 -0
- package/dist/utils/spinner.js +28 -0
- package/dist/utils/uploader.d.ts +11 -0
- package/dist/utils/uploader.js +19 -0
- package/oclif.manifest.json +237 -0
- package/package.json +82 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Andrejs Cunskis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# allure-report-publisher
|
|
2
|
+
|
|
3
|
+
[](https://hub.docker.com/r/andrcuns/allure-report-publisher)
|
|
4
|
+
[](https://hub.docker.com/r/andrcuns/allure-report-publisher)
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
Upload your report to a file storage of your choice.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
# Usage
|
|
12
|
+
|
|
13
|
+
<!-- usage -->
|
|
14
|
+
```sh-session
|
|
15
|
+
$ npm install -g allure-report-publisher
|
|
16
|
+
$ allure-report-publisher COMMAND
|
|
17
|
+
running command...
|
|
18
|
+
$ allure-report-publisher (--version)
|
|
19
|
+
allure-report-publisher/5.0.0-alpha.0 linux-x64 node-v25.2.1
|
|
20
|
+
$ allure-report-publisher --help [COMMAND]
|
|
21
|
+
USAGE
|
|
22
|
+
$ allure-report-publisher COMMAND
|
|
23
|
+
...
|
|
24
|
+
```
|
|
25
|
+
<!-- usagestop -->
|
|
26
|
+
<!-- commands -->
|
|
27
|
+
* [`allure-report-publisher upload TYPE`](#allure-report-publisher-upload-type)
|
|
28
|
+
|
|
29
|
+
## `allure-report-publisher upload TYPE`
|
|
30
|
+
|
|
31
|
+
Generate and upload allure report to cloud storage
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
USAGE
|
|
35
|
+
$ allure-report-publisher upload TYPE [--baseUrl <value>] [-r <value>] [-b <value>] [-p <value>] [-c
|
|
36
|
+
<value>] [--reportName <value>] [--ciReportTitle <value>] [--summary behaviors|suites|packages|total]
|
|
37
|
+
[--summaryTableType ascii|markdown] [--updatePr comment|description|actions] [--collapseSummary] [--color]
|
|
38
|
+
[--copyLatest] [--debug] [--flakyWarningStatus] [--ignoreMissingResults] [-o <value>] [--parallel <value>]
|
|
39
|
+
|
|
40
|
+
ARGUMENTS
|
|
41
|
+
TYPE (s3|gcs|gitlab-artifacts) Cloud storage provider type
|
|
42
|
+
|
|
43
|
+
FLAGS
|
|
44
|
+
-b, --bucket=<value> [env: ALLURE_BUCKET] Cloud storage bucket name (required for s3/gcs)
|
|
45
|
+
-c, --config=<value> [env: ALLURE_CONFIG_PATH] The path to allure config file (only .json or .yaml are
|
|
46
|
+
supported)
|
|
47
|
+
-o, --output=<value> [env: ALLURE_OUTPUT] Output directory for generated report (default: temp dir for
|
|
48
|
+
cloud, "allure-report" for gitlab-artifacts)
|
|
49
|
+
-p, --prefix=<value> [env: ALLURE_PREFIX] Prefix for report path in cloud storage (ignored for
|
|
50
|
+
gitlab-artifacts)
|
|
51
|
+
-r, --resultsGlob=<value> [default: ./**/allure-results, env: ALLURE_RESULTS_GLOB] Glob pattern for allure
|
|
52
|
+
results directories
|
|
53
|
+
--baseUrl=<value> [env: ALLURE_BASE_URL] Custom base URL for report links
|
|
54
|
+
--ciReportTitle=<value> [default: Allure Report, env: ALLURE_CI_REPORT_TITLE] Title for PR
|
|
55
|
+
comment/description section
|
|
56
|
+
--collapseSummary [env: ALLURE_COLLAPSE_SUMMARY] Create collapsible summary section in PR
|
|
57
|
+
--[no-]color [env: ALLURE_COLOR] Force color output
|
|
58
|
+
--copyLatest [env: ALLURE_COPY_LATEST] Keep copy of latest run report at base prefix (ignored for
|
|
59
|
+
gitlab-artifacts)
|
|
60
|
+
--debug [env: ALLURE_DEBUG] Print debug log output
|
|
61
|
+
--flakyWarningStatus [env: ALLURE_FLAKY_WARNING_STATUS] Mark run with ! status if flaky tests found
|
|
62
|
+
--ignoreMissingResults [env: ALLURE_IGNORE_MISSING_RESULTS] Ignore missing allure results
|
|
63
|
+
--parallel=<value> [default: 8, env: ALLURE_PARALLEL] Number of parallel threads for upload
|
|
64
|
+
--reportName=<value> [env: ALLURE_REPORT_NAME] Custom report name in Allure report (ignored with
|
|
65
|
+
config-path)
|
|
66
|
+
--summary=<option> [default: total, env: ALLURE_SUMMARY] Add test summary table to PR
|
|
67
|
+
<options: behaviors|suites|packages|total>
|
|
68
|
+
--summaryTableType=<option> [default: ascii, env: ALLURE_SUMMARY_TABLE_TYPE] Summary table format
|
|
69
|
+
<options: ascii|markdown>
|
|
70
|
+
--updatePr=<option> [env: ALLURE_UPDATE_PR] Update PR with report URL (comment/description/actions)
|
|
71
|
+
<options: comment|description|actions>
|
|
72
|
+
|
|
73
|
+
DESCRIPTION
|
|
74
|
+
Generate and upload allure report to cloud storage
|
|
75
|
+
|
|
76
|
+
EXAMPLES
|
|
77
|
+
$ allure-report-publisher upload s3 --results-glob="path/to/allure-results" --bucket=my-bucket
|
|
78
|
+
|
|
79
|
+
$ allure-report-publisher upload gcs --results-glob="paths/to/**/allure-results" --bucket=my-bucket --prefix=my-project/prs
|
|
80
|
+
|
|
81
|
+
$ allure-report-publisher upload gitlab-artifacts --results-glob="paths/to/**/allure-results"
|
|
82
|
+
|
|
83
|
+
$ allure-report-publisher upload s3 --results-glob="path/to/allure-results" --bucket=my-bucket --update-pr=comment --summary=behaviors
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
_See code: [src/commands/upload/index.ts](https://github.com/andrcuns/allure-report-publisher/blob/v5.0.0-alpha.0/src/commands/upload/index.ts)_
|
|
87
|
+
<!-- commandsstop -->
|
|
88
|
+
|
|
89
|
+
## Docker
|
|
90
|
+
|
|
91
|
+
To use cli via docker, run following command:
|
|
92
|
+
|
|
93
|
+
```sh-session
|
|
94
|
+
docker pull andrcuns/allure-report-publisher:latest
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
# Storage providers
|
|
98
|
+
|
|
99
|
+
Multiple cloud storage providers are supported
|
|
100
|
+
|
|
101
|
+
## AWS S3
|
|
102
|
+
|
|
103
|
+
Requires environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` or credentials file `~/.aws/credentials`
|
|
104
|
+
|
|
105
|
+
Additional configuration:
|
|
106
|
+
|
|
107
|
+
- `AWS_REGION`: configure s3 region, default: `us-east-1`
|
|
108
|
+
- `AWS_FORCE_PATH_STYLE`: when set to true, the bucket name is always left in the request URI and never moved to the host as a sub-domain, default: `false`
|
|
109
|
+
- `AWS_ENDPOINT`: custom s3 endpoint when used with other s3 compatible storage
|
|
110
|
+
|
|
111
|
+
## Google Cloud Storage
|
|
112
|
+
|
|
113
|
+
Requires on of the following environment variables.
|
|
114
|
+
|
|
115
|
+
credentials.json file location:
|
|
116
|
+
|
|
117
|
+
- `STORAGE_CREDENTIALS`
|
|
118
|
+
- `STORAGE_KEYFILE`
|
|
119
|
+
- `GOOGLE_CLOUD_CREDENTIALS`
|
|
120
|
+
- `GOOGLE_CLOUD_KEYFILE`
|
|
121
|
+
- `GCLOUD_KEYFILE`
|
|
122
|
+
|
|
123
|
+
credentials.json contents:
|
|
124
|
+
|
|
125
|
+
- `GOOGLE_CLOUD_CREDENTIALS_JSON`
|
|
126
|
+
- `STORAGE_CREDENTIALS_JSON`
|
|
127
|
+
- `STORAGE_KEYFILE_JSON`
|
|
128
|
+
- `GOOGLE_CLOUD_CREDENTIALS_JSON`
|
|
129
|
+
- `GOOGLE_CLOUD_KEYFILE_JSON`
|
|
130
|
+
- `GCLOUD_KEYFILE_JSON`
|
|
131
|
+
|
|
132
|
+
## Gitlab Artifacts
|
|
133
|
+
|
|
134
|
+
This storage provider is only supported for GitLab CI. Because GitLab does not expose public api for uploading artifacts, a job must be configured to upload the report as an artifact. Example:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
# .gitlab-ci.yml
|
|
138
|
+
artifacts:
|
|
139
|
+
paths:
|
|
140
|
+
- allure-report
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
where `allure-report` is the directory containing the generated Allure report and can be overridden via `--output` option.
|
|
144
|
+
|
|
145
|
+
Requires environment variable `GITLAB_AUTH_TOKEN` where token is a GitLab personal access token with `api` scope capable of downloading artifacts and retrieving job and pipeline information.
|
|
146
|
+
|
|
147
|
+
This provider is meant to be used with [GitLab CI](#gitlab-ci).
|
|
148
|
+
|
|
149
|
+
# CI
|
|
150
|
+
|
|
151
|
+
`allure-report-publisher` will automatically detect if used in CI environment and add relevant executor info and history.
|
|
152
|
+
|
|
153
|
+
Following CI providers are supported:
|
|
154
|
+
|
|
155
|
+
- Github Actions
|
|
156
|
+
- Gitlab CI
|
|
157
|
+
|
|
158
|
+
## Pull requests
|
|
159
|
+
|
|
160
|
+
It is possible to update pull requests with urls to published reports and execution summary.
|
|
161
|
+
|
|
162
|
+
- `--update-pr=(comment|description|actions)`: post report urls in pr description, as a comment or step summary for github actions
|
|
163
|
+
- `--summary=(behaviors|suites|packages|total)`: add execution summary table
|
|
164
|
+
- `--summary-table-type=(ascii|markdown)`: use markdown or ascii table formatting
|
|
165
|
+
- `--[no-]collapse-summary`: add summary in collapsable section
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
`# Allure report`
|
|
172
|
+
|
|
173
|
+
`allure-report-publisher` generated test report!
|
|
174
|
+
|
|
175
|
+
**rspec**: ✅ [test report](https://storage.googleapis.com/allure-test-reports/allure-report-publisher/refs/heads/main/index.html) for [1b756f48](https://github.com/andrcuns/allure-report-publisher/commit/HEAD)
|
|
176
|
+
|
|
177
|
+
```markdown
|
|
178
|
+
+--------------------------------------------------------+
|
|
179
|
+
| total summary |
|
|
180
|
+
+-----------+--------+--------+---------+-------+--------+
|
|
181
|
+
| | passed | failed | skipped | flaky | result |
|
|
182
|
+
+-----------+--------+--------+---------+-------+--------+
|
|
183
|
+
| Total | 100 | 0 | 2 | 0 | ✅ |
|
|
184
|
+
+-----------+--------+--------+---------+-------+--------+
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Github Actions
|
|
190
|
+
|
|
191
|
+
Additional configuration is done via environment variables
|
|
192
|
+
|
|
193
|
+
Authentication for PR updates:
|
|
194
|
+
|
|
195
|
+
- `GITHUB_AUTH_TOKEN`: github auth token with api access
|
|
196
|
+
|
|
197
|
+
Following environment variables can override default CI values:
|
|
198
|
+
|
|
199
|
+
- `ALLURE_JOB_NAME`: overrides default `GITHUB_JOB` value which is used as name for report url section
|
|
200
|
+
- `ALLURE_RUN_ID`: overrides default `GITHUB_RUN_ID` value which is used as name for the run number
|
|
201
|
+
|
|
202
|
+
### allure-publish-action
|
|
203
|
+
|
|
204
|
+
[allure-publish-action](https://github.com/marketplace/actions/allure-publish-action) can be used to easily run report publishing from any github actions job.
|
|
205
|
+
|
|
206
|
+
## Gitlab CI
|
|
207
|
+
|
|
208
|
+
Additional configuration is done via environment variables
|
|
209
|
+
|
|
210
|
+
### Authentication
|
|
211
|
+
|
|
212
|
+
Authentication for MR updates:
|
|
213
|
+
|
|
214
|
+
- `GITLAB_AUTH_TOKEN`: gitlab access token with api access
|
|
215
|
+
|
|
216
|
+
### CI values
|
|
217
|
+
|
|
218
|
+
Following environment variables can override default CI values:
|
|
219
|
+
|
|
220
|
+
- `ALLURE_JOB_NAME`: overrides default `CI_JOB_NAME` value which is used as name for report url section
|
|
221
|
+
- `ALLURE_RUN_ID`: overrides default `CI_PIPELINE_ID` value which is used as name for the run number
|
|
222
|
+
|
|
223
|
+
In case merge request triggers a downstream pipeline yet you want to update original merge request, overriding following environment variables might be useful:
|
|
224
|
+
|
|
225
|
+
- `ALLURE_PROJECT_PATH`: overrides default `CI_PROJECT_PATH` value
|
|
226
|
+
- `ALLURE_MERGE_REQUEST_IID`: overrides default `CI_MERGE_REQUEST_IID` value
|
|
227
|
+
- `ALLURE_COMMIT_SHA`: overrides default `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` or `CI_COMMIT_SHA` values
|
|
228
|
+
|
|
229
|
+
### Summary comment behavior
|
|
230
|
+
|
|
231
|
+
If reporter is executed with options `--update-pr=comment` and `--unresolved-discussion-on-failure`, it's possible to additionally configure the unresolved discussion note:
|
|
232
|
+
|
|
233
|
+
- `ALLURE_FAILURE_ALERT_COMMENT`: comment added to create unresolved discussion note, default: `There are some test failures that need attention`
|
|
234
|
+
|
|
235
|
+
### CI/CD catalog resource
|
|
236
|
+
|
|
237
|
+
[allure-report-publisher CI/CD catalog resource](https://gitlab.com/andrcuns/allure-report-publisher) can be used to easily integrate report publishing in to Gitlab CI pipelines.
|
|
238
|
+
|
|
239
|
+
# Development
|
|
240
|
+
|
|
241
|
+
Local development tool are handled by [mise](https://mise.jdx.dev/). After checking out the repo, run `mise install` to install necessary dev tools. Run `pnpm install` to install all node dependencies. To run tests, use `pnpm run test`. `bin/dev.js` allows to execute the cli directly from the source code without building it first.
|
|
242
|
+
|
|
243
|
+
# Contributing
|
|
244
|
+
|
|
245
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/andrcuns/allure-report-publisher>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/andrcuns/allure-report-publisher/blob/main/CODE_OF_CONDUCT.md).
|
|
246
|
+
|
|
247
|
+
# License
|
|
248
|
+
|
|
249
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
250
|
+
|
|
251
|
+
# Code of Conduct
|
|
252
|
+
|
|
253
|
+
Everyone interacting in the allure-report-publisher project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/andrcuns/allure-report-publisher/blob/main/CODE_OF_CONDUCT.md).
|
package/bin/dev.cmd
ADDED
package/bin/dev.js
ADDED
package/bin/run.cmd
ADDED
package/bin/run.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Upload extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
type: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
baseUrl: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
resultsGlob: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
bucket: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
prefix: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
reportName: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
ciReportTitle: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
summary: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
summaryTableType: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
updatePr: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
collapseSummary: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
20
|
+
color: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
21
|
+
copyLatest: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
|
+
debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
23
|
+
flakyWarningStatus: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
24
|
+
ignoreMissingResults: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
25
|
+
output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
26
|
+
parallel: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
27
|
+
};
|
|
28
|
+
run(): Promise<void>;
|
|
29
|
+
private validateInputs;
|
|
30
|
+
private getAllureResults;
|
|
31
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getAllureConfig } from '../../lib/allure/config.js';
|
|
5
|
+
import { ReportGenerator } from '../../lib/allure/report-generator.js';
|
|
6
|
+
import { config } from '../../utils/config.js';
|
|
7
|
+
import { getAllureResultsPaths } from '../../utils/glob.js';
|
|
8
|
+
import { logger } from '../../utils/logger.js';
|
|
9
|
+
import { spin } from '../../utils/spinner.js';
|
|
10
|
+
import { getUploader } from '../../utils/uploader.js';
|
|
11
|
+
export default class Upload extends Command {
|
|
12
|
+
static args = {
|
|
13
|
+
type: Args.string({
|
|
14
|
+
description: 'Cloud storage provider type',
|
|
15
|
+
options: ['s3', 'gcs', 'gitlab-artifacts'],
|
|
16
|
+
required: true,
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
static description = 'Generate and upload allure report to cloud storage';
|
|
20
|
+
static examples = [
|
|
21
|
+
'<%= config.bin %> <%= command.id %> s3 --results-glob="path/to/allure-results" --bucket=my-bucket',
|
|
22
|
+
'<%= config.bin %> <%= command.id %> gcs --results-glob="paths/to/**/allure-results" --bucket=my-bucket --prefix=my-project/prs',
|
|
23
|
+
'<%= config.bin %> <%= command.id %> gitlab-artifacts --results-glob="paths/to/**/allure-results"',
|
|
24
|
+
'<%= config.bin %> <%= command.id %> s3 --results-glob="path/to/allure-results" --bucket=my-bucket --update-pr=comment --summary=behaviors',
|
|
25
|
+
];
|
|
26
|
+
static flags = {
|
|
27
|
+
baseUrl: Flags.string({
|
|
28
|
+
aliases: ['base-url'],
|
|
29
|
+
description: 'Custom base URL for report links',
|
|
30
|
+
env: 'ALLURE_BASE_URL',
|
|
31
|
+
}),
|
|
32
|
+
resultsGlob: Flags.string({
|
|
33
|
+
aliases: ['results-glob'],
|
|
34
|
+
char: 'r',
|
|
35
|
+
default: './**/allure-results',
|
|
36
|
+
description: 'Glob pattern for allure results directories',
|
|
37
|
+
env: 'ALLURE_RESULTS_GLOB',
|
|
38
|
+
}),
|
|
39
|
+
bucket: Flags.string({
|
|
40
|
+
char: 'b',
|
|
41
|
+
description: 'Cloud storage bucket name (required for s3/gcs)',
|
|
42
|
+
env: 'ALLURE_BUCKET',
|
|
43
|
+
}),
|
|
44
|
+
prefix: Flags.string({
|
|
45
|
+
char: 'p',
|
|
46
|
+
description: 'Prefix for report path in cloud storage (ignored for gitlab-artifacts)',
|
|
47
|
+
env: 'ALLURE_PREFIX',
|
|
48
|
+
}),
|
|
49
|
+
config: Flags.string({
|
|
50
|
+
char: 'c',
|
|
51
|
+
description: 'The path to allure config file (only .json or .yaml are supported)',
|
|
52
|
+
env: 'ALLURE_CONFIG_PATH',
|
|
53
|
+
}),
|
|
54
|
+
reportName: Flags.string({
|
|
55
|
+
aliases: ['report-name'],
|
|
56
|
+
description: 'Custom report name in Allure report (ignored with config-path)',
|
|
57
|
+
env: 'ALLURE_REPORT_NAME',
|
|
58
|
+
}),
|
|
59
|
+
ciReportTitle: Flags.string({
|
|
60
|
+
aliases: ['ci-report-title'],
|
|
61
|
+
default: 'Allure Report',
|
|
62
|
+
description: 'Title for PR comment/description section',
|
|
63
|
+
env: 'ALLURE_CI_REPORT_TITLE',
|
|
64
|
+
}),
|
|
65
|
+
summary: Flags.string({
|
|
66
|
+
default: 'total',
|
|
67
|
+
description: 'Add test summary table to PR',
|
|
68
|
+
env: 'ALLURE_SUMMARY',
|
|
69
|
+
options: ['behaviors', 'suites', 'packages', 'total'],
|
|
70
|
+
}),
|
|
71
|
+
summaryTableType: Flags.string({
|
|
72
|
+
aliases: ['summary-table-type'],
|
|
73
|
+
default: 'ascii',
|
|
74
|
+
description: 'Summary table format',
|
|
75
|
+
env: 'ALLURE_SUMMARY_TABLE_TYPE',
|
|
76
|
+
options: ['ascii', 'markdown'],
|
|
77
|
+
}),
|
|
78
|
+
updatePr: Flags.string({
|
|
79
|
+
aliases: ['update-pr'],
|
|
80
|
+
description: 'Update PR with report URL (comment/description/actions)',
|
|
81
|
+
env: 'ALLURE_UPDATE_PR',
|
|
82
|
+
options: ['comment', 'description', 'actions'],
|
|
83
|
+
}),
|
|
84
|
+
// Boolean flags
|
|
85
|
+
collapseSummary: Flags.boolean({
|
|
86
|
+
aliases: ['collapse-summary'],
|
|
87
|
+
default: false,
|
|
88
|
+
description: 'Create collapsible summary section in PR',
|
|
89
|
+
env: 'ALLURE_COLLAPSE_SUMMARY',
|
|
90
|
+
}),
|
|
91
|
+
color: Flags.boolean({
|
|
92
|
+
allowNo: true,
|
|
93
|
+
description: 'Force color output',
|
|
94
|
+
env: 'ALLURE_COLOR',
|
|
95
|
+
}),
|
|
96
|
+
copyLatest: Flags.boolean({
|
|
97
|
+
aliases: ['copy-latest'],
|
|
98
|
+
default: false,
|
|
99
|
+
description: 'Keep copy of latest run report at base prefix (ignored for gitlab-artifacts)',
|
|
100
|
+
env: 'ALLURE_COPY_LATEST',
|
|
101
|
+
}),
|
|
102
|
+
debug: Flags.boolean({
|
|
103
|
+
default: false,
|
|
104
|
+
description: 'Print debug log output',
|
|
105
|
+
env: 'ALLURE_DEBUG',
|
|
106
|
+
}),
|
|
107
|
+
flakyWarningStatus: Flags.boolean({
|
|
108
|
+
aliases: ['flaky-warning-status'],
|
|
109
|
+
default: false,
|
|
110
|
+
description: 'Mark run with ! status if flaky tests found',
|
|
111
|
+
env: 'ALLURE_FLAKY_WARNING_STATUS',
|
|
112
|
+
}),
|
|
113
|
+
ignoreMissingResults: Flags.boolean({
|
|
114
|
+
aliases: ['ignore-missing-results'],
|
|
115
|
+
default: false,
|
|
116
|
+
description: 'Ignore missing allure results',
|
|
117
|
+
env: 'ALLURE_IGNORE_MISSING_RESULTS',
|
|
118
|
+
}),
|
|
119
|
+
output: Flags.string({
|
|
120
|
+
char: 'o',
|
|
121
|
+
description: 'Output directory for generated report (default: temp dir for cloud, "allure-report" for gitlab-artifacts)',
|
|
122
|
+
env: 'ALLURE_OUTPUT',
|
|
123
|
+
}),
|
|
124
|
+
parallel: Flags.integer({
|
|
125
|
+
default: 8,
|
|
126
|
+
description: 'Number of parallel threads for upload',
|
|
127
|
+
env: 'ALLURE_PARALLEL',
|
|
128
|
+
}),
|
|
129
|
+
};
|
|
130
|
+
async run() {
|
|
131
|
+
const { args, flags } = await this.parse(Upload);
|
|
132
|
+
const colorEnabled = flags.color ?? process.stdout.isTTY;
|
|
133
|
+
config.initialize({ color: colorEnabled, debug: flags.debug, parallel: flags.parallel });
|
|
134
|
+
try {
|
|
135
|
+
const storageType = args.type;
|
|
136
|
+
await this.validateInputs(storageType, flags);
|
|
137
|
+
logger.section('Generating allure report');
|
|
138
|
+
const resultPaths = await this.getAllureResults(flags.resultsGlob, flags.ignoreMissingResults);
|
|
139
|
+
if (resultPaths === undefined)
|
|
140
|
+
return;
|
|
141
|
+
const allureConfig = getAllureConfig(flags.config, flags.reportName);
|
|
142
|
+
const uploader = getUploader(storageType, {
|
|
143
|
+
baseUrl: flags.baseUrl,
|
|
144
|
+
bucket: flags.bucket,
|
|
145
|
+
copyLatest: flags.copyLatest,
|
|
146
|
+
parallel: flags.parallel,
|
|
147
|
+
prefix: flags.prefix,
|
|
148
|
+
output: await allureConfig.outputPath(),
|
|
149
|
+
historyPath: await allureConfig.historyPath(),
|
|
150
|
+
plugins: await allureConfig.plugins(),
|
|
151
|
+
});
|
|
152
|
+
const reportGenerator = new ReportGenerator(flags.resultsGlob, allureConfig, () => uploader.downloadHistory());
|
|
153
|
+
await reportGenerator.execute();
|
|
154
|
+
logger.section(`Uploading report to ${storageType}`);
|
|
155
|
+
await uploader.upload();
|
|
156
|
+
// TODO: Update PR if requested
|
|
157
|
+
if (flags.updatePr) {
|
|
158
|
+
logger.section('Updating PR/MR');
|
|
159
|
+
logger.info('PR update not yet implemented');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
logger.error(error.message);
|
|
164
|
+
this.exit(1);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async validateInputs(type, flags) {
|
|
168
|
+
if (type !== 'gitlab-artifacts' && !flags.bucket) {
|
|
169
|
+
throw new Error(`--bucket is required for storage type "${type}"\nOnly gitlab-artifacts does not require a bucket.`);
|
|
170
|
+
}
|
|
171
|
+
if (flags.baseUrl) {
|
|
172
|
+
try {
|
|
173
|
+
// eslint-disable-next-line no-new
|
|
174
|
+
new URL(flags.baseUrl);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
throw new Error(`Invalid base URL: ${flags.baseUrl}\nBase URL must be a valid URL starting with http:// or https://`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (flags.parallel < 1) {
|
|
181
|
+
throw new Error(`Invalid parallel threads: ${flags.parallel}\nParallel threads must be >= 1`);
|
|
182
|
+
}
|
|
183
|
+
if (flags.config) {
|
|
184
|
+
const ext = path.extname(flags.config).toLowerCase();
|
|
185
|
+
const supportedExts = ['.json', '.yaml'];
|
|
186
|
+
if (!supportedExts.includes(ext)) {
|
|
187
|
+
throw new Error(`Unsupported config file format: ${ext}\nSupported formats are: ${supportedExts.join(', ')}`);
|
|
188
|
+
}
|
|
189
|
+
if (existsSync(flags.config) === false) {
|
|
190
|
+
throw new Error(`Config file not found at path: ${flags.config}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async getAllureResults(resultsGlob, ignoreMissingResults) {
|
|
195
|
+
const resultPaths = await spin(getAllureResultsPaths(resultsGlob, ignoreMissingResults), `scanning allure results directories`, { ignoreError: ignoreMissingResults });
|
|
196
|
+
return resultPaths;
|
|
197
|
+
}
|
|
198
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PluginName } from '../../types/index.js';
|
|
2
|
+
export interface AllureConfig {
|
|
3
|
+
configPath(): string;
|
|
4
|
+
historyPath(): Promise<string>;
|
|
5
|
+
outputPath(): Promise<string>;
|
|
6
|
+
plugins(): Promise<PluginName[]>;
|
|
7
|
+
}
|
|
8
|
+
export declare function getAllureConfig(configPath?: string, reportName?: string): AllureConfig;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import yaml from 'yaml';
|
|
6
|
+
import { logger } from '../../utils/logger.js';
|
|
7
|
+
import { spin } from '../../utils/spinner.js';
|
|
8
|
+
const defaultReportBasePath = path.join(os.tmpdir(), 'allure-report-publisher');
|
|
9
|
+
const defaultConfig = {
|
|
10
|
+
output: path.join(defaultReportBasePath, 'allure-report'),
|
|
11
|
+
historyPath: path.join(defaultReportBasePath, 'history.jsonl'),
|
|
12
|
+
appendHistory: true,
|
|
13
|
+
plugins: {
|
|
14
|
+
awesome: {
|
|
15
|
+
options: {
|
|
16
|
+
enabled: true,
|
|
17
|
+
singleFile: true,
|
|
18
|
+
reportName: 'Test Report',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
class CustomConfig {
|
|
24
|
+
_configPath;
|
|
25
|
+
_parsedConfig = defaultConfig;
|
|
26
|
+
constructor(configPath) {
|
|
27
|
+
this._configPath = configPath;
|
|
28
|
+
}
|
|
29
|
+
configPath() {
|
|
30
|
+
return this._configPath;
|
|
31
|
+
}
|
|
32
|
+
async historyPath() {
|
|
33
|
+
const config = await this.customConfig();
|
|
34
|
+
const path = config.historyPath;
|
|
35
|
+
if (!path)
|
|
36
|
+
throw new Error('History path is not defined in the allure config');
|
|
37
|
+
return path;
|
|
38
|
+
}
|
|
39
|
+
async outputPath() {
|
|
40
|
+
const config = await this.customConfig();
|
|
41
|
+
return config.output || defaultConfig.output;
|
|
42
|
+
}
|
|
43
|
+
async plugins() {
|
|
44
|
+
const config = await this.customConfig();
|
|
45
|
+
const plugins = new Set(['allure2', 'awesome', 'classic', 'csv', 'dashboard']);
|
|
46
|
+
const configPlugins = config?.plugins || defaultConfig.plugins;
|
|
47
|
+
return Object.entries(configPlugins)
|
|
48
|
+
.filter(([pluginName, config]) => plugins.has(pluginName) && (config.enabled ?? true))
|
|
49
|
+
.map(([pluginName]) => pluginName);
|
|
50
|
+
}
|
|
51
|
+
async customConfig() {
|
|
52
|
+
if (this._parsedConfig === defaultConfig) {
|
|
53
|
+
this._parsedConfig = await spin(this.loadConfig(), 'loading custom allure config');
|
|
54
|
+
}
|
|
55
|
+
return this._parsedConfig;
|
|
56
|
+
}
|
|
57
|
+
async loadConfig() {
|
|
58
|
+
const ext = path.extname(this._configPath).toLowerCase();
|
|
59
|
+
switch (ext) {
|
|
60
|
+
case '.cjs':
|
|
61
|
+
case '.js':
|
|
62
|
+
case '.mjs': {
|
|
63
|
+
const fileUrl = pathToFileURL(this._configPath).href;
|
|
64
|
+
const module = await import(fileUrl);
|
|
65
|
+
// module.default will contain the object returned by defineConfig()
|
|
66
|
+
// allure loads parser with default config setup which will create error in the output
|
|
67
|
+
// plain object should be exported to avoid that
|
|
68
|
+
const defaultConfig = module.default;
|
|
69
|
+
if (defaultConfig === undefined) {
|
|
70
|
+
throw new Error(`No default export found in the config file: ${this._configPath}`);
|
|
71
|
+
}
|
|
72
|
+
logger.debug(`Loaded JS config: ${JSON.stringify(defaultConfig, null, 2)}`);
|
|
73
|
+
return defaultConfig;
|
|
74
|
+
}
|
|
75
|
+
case '.json': {
|
|
76
|
+
const content = JSON.parse(readFileSync(this._configPath, 'utf8'));
|
|
77
|
+
logger.debug(`Loaded JSON config: ${JSON.stringify(content, null, 2)}`);
|
|
78
|
+
return content;
|
|
79
|
+
}
|
|
80
|
+
case '.yaml': {
|
|
81
|
+
const content = yaml.parse(readFileSync(this._configPath, 'utf8'));
|
|
82
|
+
logger.debug(`Loaded YAML config: ${JSON.stringify(content, null, 2)}`);
|
|
83
|
+
return content;
|
|
84
|
+
}
|
|
85
|
+
default: {
|
|
86
|
+
throw new Error(`Unsupported config file format: ${ext}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
class DefaultConfig {
|
|
92
|
+
_configCreated;
|
|
93
|
+
_configPath;
|
|
94
|
+
reportName;
|
|
95
|
+
constructor(reportName) {
|
|
96
|
+
this._configCreated = false;
|
|
97
|
+
this._configPath = path.join(defaultReportBasePath, 'allurerc.json');
|
|
98
|
+
this.reportName = reportName;
|
|
99
|
+
}
|
|
100
|
+
configPath() {
|
|
101
|
+
if (this._configCreated)
|
|
102
|
+
return this._configPath;
|
|
103
|
+
mkdirSync(defaultReportBasePath, { recursive: true });
|
|
104
|
+
const config = { ...defaultConfig };
|
|
105
|
+
if (this.reportName)
|
|
106
|
+
config.plugins.awesome.options.reportName = this.reportName;
|
|
107
|
+
writeFileSync(this._configPath, JSON.stringify(config, null, 2));
|
|
108
|
+
this._configCreated = true;
|
|
109
|
+
return this._configPath;
|
|
110
|
+
}
|
|
111
|
+
async plugins() {
|
|
112
|
+
return ['awesome'];
|
|
113
|
+
}
|
|
114
|
+
async historyPath() {
|
|
115
|
+
return defaultConfig.historyPath;
|
|
116
|
+
}
|
|
117
|
+
async outputPath() {
|
|
118
|
+
return defaultConfig.output;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export function getAllureConfig(configPath, reportName) {
|
|
122
|
+
if (configPath)
|
|
123
|
+
return new CustomConfig(configPath);
|
|
124
|
+
return new DefaultConfig(reportName);
|
|
125
|
+
}
|