From b8e93b95906d47167616a566c870343247fe9a66 Mon Sep 17 00:00:00 2001 From: Max Bauer Date: Mon, 9 Sep 2024 09:08:23 +0200 Subject: [PATCH] Squash commit of krummbar/bruno-run-action commit 0580b4cbe3cbbe5c9e25eb2476512b0d86c379ef Author: Max Bauer Date: Sun Sep 8 21:05:35 2024 +0200 fix: check bool arguments for actual true value commit b02789d291e7f2b13463ff1f09989137822d6baa Author: Max Bauer Date: Sun Sep 8 21:05:19 2024 +0200 fix: remove default for sandbox commit fba9bbff90f50d52081dac8a3e0e5ae5f0d7fdf8 Author: Max Bauer Date: Sun Sep 1 08:16:03 2024 +0200 fix(lint): .github/workflows/ci.env commit 839163a3bf7d57ff6f418fec41be99d7662088ab Author: Max Bauer Date: Sun Sep 1 08:11:41 2024 +0200 feat: add --ignore-truststore and --sandbox arguments commit 5f0c216a37284a12095c9508ff6519e446b59906 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat Aug 31 10:58:31 2024 +0200 build(deps): bump super-linter/super-linter from 6 to 7 (#4) * build(deps): bump super-linter/super-linter from 6 to 7 Bumps [super-linter/super-linter](https://github.com/super-linter/super-linter) from 6 to 7. - [Release notes](https://github.com/super-linter/super-linter/releases) - [Changelog](https://github.com/super-linter/super-linter/blob/main/CHANGELOG.md) - [Commits](https://github.com/super-linter/super-linter/compare/v6...v7) --- updated-dependencies: - dependency-name: super-linter/super-linter dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * fix .github/workflows/ci.env * fix prettier to ignore bruno collection * fix yaml formatter * fix action.yaml formatter issues * fix .prettierrc.json issues --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Max Bauer commit 59483ab28eb9fd66daf79f68e72a6c13f7956afc Author: Max Bauer Date: Sat Jul 20 14:43:28 2024 +0200 feat: add csv-file-path input argument (#3) feat: add output parameter bru-version feat: add input argumentcsv-file-path chore: simplified bruno collection used during CI commit 362f5c8e1ca47f20c6c3b9706030af6c4dc035fc Author: Max Bauer Date: Sun Jul 14 06:54:49 2024 +0200 feat: pin bru cli to version 1.x commit 431a511202f8f93595726f9d6a6ff372a17caf43 Author: Max Bauer Date: Sun Jun 30 08:07:46 2024 +0200 feat: change camelCase to kebap-case (#2) * feat: change to camelCase to kebap-case commit 4e11dfb9e7dbde72e2dce3559a9d548c991e2ab5 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun Jun 30 08:01:09 2024 +0200 Bump docker/build-push-action from 5 to 6 (#1) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5...v6) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit 7be7ca62a0705f6c1288e1b0631c7416634b2d28 Author: Max Bauer Date: Sun Jun 9 07:23:50 2024 +0200 feat: enable cacert & updated docs commit 77c7c7959811bdd60740fdafc5cce55cdb2405cb Author: Max Bauer Date: Sat Jun 8 07:33:35 2024 +0200 optimize entrypoint.sh commit 14d481bd0e681abc965f29dc0fb775c52975e4e6 Author: Max Bauer Date: Tue Jun 4 22:53:25 2024 +0200 use bruno sanity test environment for cicd commit 12e3a1addb4fd3b3ba4b6fc8375c0d86afa904ed Author: Max Bauer Date: Sun Jun 2 09:08:07 2024 +0200 inital draft commit 417d61c189de558a9bc8347bf8efbdb377cb10d3 Author: Max Bauer Date: Thu May 30 21:09:21 2024 +0200 initial commit --- .editorconfig | 19 ++ .gitattributes | 1 + .github/bruno-collection/bruno.json | 9 + .github/bruno-collection/collection.bru | 0 .github/bruno-collection/dummy.csv | 1 + .github/bruno-collection/echo/echo json.bru | 31 +++ .../bruno-collection/echo/echo plaintext.bru | 27 ++ .../bruno-collection/echo/echo xml parsed.bru | 35 +++ .../echo/secret/echo secret.bru | 39 +++ .../bruno-collection/environments/cicd.bru | 7 + .github/bruno-collection/ping.bru | 23 ++ .github/dependabot.yml | 21 ++ .github/linters/.checkov.yaml | 6 + .github/linters/.hadolint.yaml | 3 + .github/linters/.yaml-lint.yml | 10 + .github/workflows/ci.env | 15 + .github/workflows/ci.yml | 129 +++++++++ .github/workflows/linter.yml | 34 +++ .gitignore | 25 ++ .prettierignore | 1 + .prettierrc.json | 16 ++ CODEOWNERS | 3 + Dockerfile | 17 ++ README.adoc | 260 ++++++++++++++++++ action.yml | 103 +++++++ docs/documentation.adoc | 18 ++ docs/edit-advise.adoc | 7 + docs/examples/usage-single-folder.yaml | 17 ++ docs/examples/usage.yaml | 21 ++ docs/index.adoc | 36 +++ docs/test-local.adoc | 47 ++++ docs/usage.adoc | 39 +++ entrypoint.sh | 154 +++++++++++ 33 files changed, 1174 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/bruno-collection/bruno.json create mode 100644 .github/bruno-collection/collection.bru create mode 100644 .github/bruno-collection/dummy.csv create mode 100644 .github/bruno-collection/echo/echo json.bru create mode 100644 .github/bruno-collection/echo/echo plaintext.bru create mode 100644 .github/bruno-collection/echo/echo xml parsed.bru create mode 100644 .github/bruno-collection/echo/secret/echo secret.bru create mode 100644 .github/bruno-collection/environments/cicd.bru create mode 100644 .github/bruno-collection/ping.bru create mode 100644 .github/dependabot.yml create mode 100644 .github/linters/.checkov.yaml create mode 100644 .github/linters/.hadolint.yaml create mode 100644 .github/linters/.yaml-lint.yml create mode 100644 .github/workflows/ci.env create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/linter.yml create mode 100644 .gitignore create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 CODEOWNERS create mode 100644 Dockerfile create mode 100644 README.adoc create mode 100644 action.yml create mode 100644 docs/documentation.adoc create mode 100644 docs/edit-advise.adoc create mode 100644 docs/examples/usage-single-folder.yaml create mode 100644 docs/examples/usage.yaml create mode 100644 docs/index.adoc create mode 100644 docs/test-local.adoc create mode 100644 docs/usage.adoc create mode 100755 entrypoint.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5693a52 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +insert_final_newline = true + +[*.json] +insert_final_newline = true + +[*.bru] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/bruno-collection/bruno.json b/.github/bruno-collection/bruno.json new file mode 100644 index 0000000..3e118a2 --- /dev/null +++ b/.github/bruno-collection/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "bruno-run-action-tests", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} diff --git a/.github/bruno-collection/collection.bru b/.github/bruno-collection/collection.bru new file mode 100644 index 0000000..e69de29 diff --git a/.github/bruno-collection/dummy.csv b/.github/bruno-collection/dummy.csv new file mode 100644 index 0000000..47929ee --- /dev/null +++ b/.github/bruno-collection/dummy.csv @@ -0,0 +1 @@ +foo,bar diff --git a/.github/bruno-collection/echo/echo json.bru b/.github/bruno-collection/echo/echo json.bru new file mode 100644 index 0000000..298c53f --- /dev/null +++ b/.github/bruno-collection/echo/echo json.bru @@ -0,0 +1,31 @@ +meta { + name: echo json + type: http + seq: 1 +} + +post { + url: {{host}}/api/echo/json + body: json + auth: none +} + +body:json { + { + "hello": "bruno" + } +} + +assert { + res.status: eq 200 +} + +tests { + test("should return json", function() { + const data = res.getBody(); + expect(data).to.eql({ + "hello": "bruno" + }); + }); + +} diff --git a/.github/bruno-collection/echo/echo plaintext.bru b/.github/bruno-collection/echo/echo plaintext.bru new file mode 100644 index 0000000..f2a7b98 --- /dev/null +++ b/.github/bruno-collection/echo/echo plaintext.bru @@ -0,0 +1,27 @@ +meta { + name: echo plaintext + type: http + seq: 3 +} + +post { + url: {{host}}/api/echo/text + body: text + auth: none +} + +body:text { + hello +} + +assert { + res.status: eq 200 +} + +tests { + test("should return plain text", function() { + const data = res.getBody(); + expect(data).to.eql("hello"); + }); + +} diff --git a/.github/bruno-collection/echo/echo xml parsed.bru b/.github/bruno-collection/echo/echo xml parsed.bru new file mode 100644 index 0000000..4adea51 --- /dev/null +++ b/.github/bruno-collection/echo/echo xml parsed.bru @@ -0,0 +1,35 @@ +meta { + name: echo xml parsed + type: http + seq: 2 +} + +post { + url: {{host}}/api/echo/xml-parsed + body: xml + auth: none +} + +body:xml { + + bruno + +} + +assert { + res.status: eq 200 +} + +tests { + test("should return parsed xml", function() { + const data = res.getBody(); + expect(data).to.eql({ + "hello": { + "world": [ + "bruno" + ] + } + }); + }); + +} diff --git a/.github/bruno-collection/echo/secret/echo secret.bru b/.github/bruno-collection/echo/secret/echo secret.bru new file mode 100644 index 0000000..f87b75a --- /dev/null +++ b/.github/bruno-collection/echo/secret/echo secret.bru @@ -0,0 +1,39 @@ +meta { + name: echo secret + type: http + seq: 1 +} + +post { + url: {{host}}/api/echo/json + body: json + auth: none +} + +auth:basic { + username: asd + password: j +} + +auth:bearer { + token: +} + +body:json { + { + "{{secretKey}}": "{{secretMessage}}" + } +} + +assert { + res.status: eq 200 +} + +tests { + test("should return secret message", () => { + expect(res.getBody()).to.eql({ + "hello": "secret world!" + }); + }); + +} diff --git a/.github/bruno-collection/environments/cicd.bru b/.github/bruno-collection/environments/cicd.bru new file mode 100644 index 0000000..6d14934 --- /dev/null +++ b/.github/bruno-collection/environments/cicd.bru @@ -0,0 +1,7 @@ +vars { + host: https://testbench-sanity.usebruno.com +} +vars:secret [ + secretKey, + secretMessage +] diff --git a/.github/bruno-collection/ping.bru b/.github/bruno-collection/ping.bru new file mode 100644 index 0000000..c832e72 --- /dev/null +++ b/.github/bruno-collection/ping.bru @@ -0,0 +1,23 @@ +meta { + name: ping + type: http + seq: 1 +} + +get { + url: {{host}}/ping + body: none + auth: none +} + +assert { + res.status: eq 200 + ~res.body: eq {{pong}} +} + +tests { + test("should ping pong", function() { + const data = res.getBody(); + expect(data).to.equal("pong"); + }); +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..9d98c2a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,21 @@ +version: 2 +updates: + - package-ecosystem: docker + directory: / + schedule: + interval: weekly + groups: + docker-minor: + update-types: + - minor + - patch + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + groups: + actions-minor: + update-types: + - minor + - patch diff --git a/.github/linters/.checkov.yaml b/.github/linters/.checkov.yaml new file mode 100644 index 0000000..c306051 --- /dev/null +++ b/.github/linters/.checkov.yaml @@ -0,0 +1,6 @@ +quiet: true +skip-check: + # Ensure that HEALTHCHECK instructions have been added to container images + - CKV_DOCKER_2 + # Ensure that a user for the container has been created + - CKV_DOCKER_3 diff --git a/.github/linters/.hadolint.yaml b/.github/linters/.hadolint.yaml new file mode 100644 index 0000000..eee9f9b --- /dev/null +++ b/.github/linters/.hadolint.yaml @@ -0,0 +1,3 @@ +ignored: + - DL3018 + - DL3059 diff --git a/.github/linters/.yaml-lint.yml b/.github/linters/.yaml-lint.yml new file mode 100644 index 0000000..561b6de --- /dev/null +++ b/.github/linters/.yaml-lint.yml @@ -0,0 +1,10 @@ +rules: + document-end: disable + document-start: + level: warning + present: false + line-length: + level: warning + max: 100 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true diff --git a/.github/workflows/ci.env b/.github/workflows/ci.env new file mode 100644 index 0000000..1a5e341 --- /dev/null +++ b/.github/workflows/ci.env @@ -0,0 +1,15 @@ +BRUNO_ACTION_DRY_RUN=true +IN_BAIL=true +IN_CA_CERT=customcert.pem +IN_CSV_FILEPATH=.github/bruno-collection/dummy.csv +IN_ENV=cicd +IN_ENV_VARS=apikey=myPassword\nid=myId +IN_FILENAME=users/get-user.bru +IN_IGNORE_TRUSTSTORE=true +IN_INSECURE=true +IN_OUTPUT=output.html +IN_OUTPUT_FORMAT=html +IN_PATH=. +IN_RECURSIVE=true +IN_SANDBOX=safe +IN_TESTS_ONLY=true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..31b1761 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,129 @@ +name: Continuous Integration + +on: + pull_request: + branches: + - main + push: + branches: + - main + +permissions: + contents: read + +jobs: + test-docker: + name: Docker Tests + runs-on: ubuntu-latest + + # Run a local registry to push to + services: + registry: + image: registry:2 + ports: + - 5001:5000 + + env: + TEST_TAG: localhost:5001/actions/bruno-run-action:latest + RUN_OUTPUT: run-output.log + + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + + - name: Setup Docker BuildX + id: setup-buildx + uses: docker/setup-buildx-action@v3 + with: + install: true + driver-opts: network=host + + - name: Build the Container + id: build + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ env.TEST_TAG }} + + # prettier-ignore + - name: Run the Container + id: run + run: docker run --env-file .github/workflows/ci.env --rm ${{ env.TEST_TAG }} > ${{ env.RUN_OUTPUT }} + + # prettier-ignore + - name: Assert output + id: assert + run: | + cat ${{ env.RUN_OUTPUT }} + grep -q 'run users/get-user.bru' ${{ env.RUN_OUTPUT }} || (echo '::error::incorrect collection path' && exit 1) + grep -q '\-r' ${{ env.RUN_OUTPUT }} || (echo '::error::-r was not supplied' && exit 1) + grep -q '\-\-ignore\-truststore' ${{ env.RUN_OUTPUT }} || (echo '::error::--ignore-truststore was not applied' && exit 1) + grep -q '\-\-env cicd' ${{ env.RUN_OUTPUT }} || (echo '::error::incorrect --env supplied' && exit 1) + grep -q '\-\-env\-var apikey=myPassword' ${{ env.RUN_OUTPUT }} || (echo '::error::incorrect --env-var for apikey' && exit 1) + grep -q '\-\-env\-var id=myId' ${{ env.RUN_OUTPUT }} || (echo '::error::incorrect --env-var for id' && exit 1) + grep -q '\-\-output /usr/src/output.html' ${{ env.RUN_OUTPUT }} || (echo '::error::incorrect --output path' && exit 1) + grep -q '\-\-format html' ${{ env.RUN_OUTPUT }} || (echo '::error::incorrect --format' && exit 1) + grep -q '\-\-cacert customcert.pem' ${{ env.RUN_OUTPUT }} || (echo '::error::incorrect --ca-cert path' && exit 1) + grep -q '\-\-insecure' ${{ env.RUN_OUTPUT }} || (echo '::error::--insecure was not applied' && exit 1) + grep -q '\-\-sandbox safe' ${{ env.RUN_OUTPUT }} || (echo '::error::--sandbox was not supplied' && exit 1) + grep -q '\-\-tests\-only' ${{ env.RUN_OUTPUT }} || (echo '::error::--tests-only was not supplied' && exit 1) + grep -q '\-\-csv\-file\-path' ${{ env.RUN_OUTPUT }} || (echo '::error::--csv-file-path was not supplied' && exit 1) + grep -q '\-\-bail' ${{ env.RUN_OUTPUT }} || (echo '::error::--bail was not supplied' && exit 1) + + bru-run-successfully: + name: Action Successful Run + runs-on: ubuntu-latest + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + + - name: Test Local Action + id: bru-run + uses: ./ + with: + path: .github/bruno-collection + env: cicd + env-vars: |- + secretKey='${{ secrets.CI_TEST_SECRET_KEY }}' + secretMessage='${{ secrets.CI_TEST_SECRET_MESSAGE }}' + output: bru-run-output.json + output-format: json + insecure: true + tests-only: false + bail: false + + - name: Succeed if run was successful + if: ${{ steps.bru-run.outputs.success == 'true' }} + run: exit 0 + + - name: Fail if run was not successful + if: ${{ steps.bru-run.outputs.success == 'false' }} + run: exit 1 + + bru-run-failure: + name: Action Failure Run + runs-on: ubuntu-latest + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + + - name: Test Local Action + id: bru-run + continue-on-error: true + uses: ./ + with: + path: .github/bruno-collection + tests-only: false + bail: true + + - name: Succeed if run was not successful + if: ${{ steps.bru-run.outputs.success == 'false' }} + run: exit 0 + + - name: Fail if run was successful + if: ${{ steps.bru-run.outputs.success == 'true' }} + run: exit 1 diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 0000000..4c88696 --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,34 @@ +name: Lint Codebase + +on: + pull_request: + branches: + - main + push: + branches: + - main + +permissions: + contents: read + packages: read + statuses: write + +jobs: + lint: + name: Lint Codebase + runs-on: ubuntu-latest + + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Lint Codebase + id: super-linter + uses: super-linter/super-linter/slim@v7 + env: + DEFAULT_BRANCH: main + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VALIDATE_ALL_CODEBASE: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dcff431 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# dotenv environment variables file +.env +.env.test + +# OS metadata +.DS_Store +Thumbs.db + +# IDE files +.idea +.vscode +*.code-workspace diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..3bfd435 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +.github/bruno-collection diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..b2154c2 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,16 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "semi": false, + "singleQuote": true, + "quoteProps": "as-needed", + "jsxSingleQuote": false, + "trailingComma": "none", + "bracketSpacing": true, + "bracketSameLine": true, + "arrowParens": "avoid", + "proseWrap": "always", + "htmlWhitespaceSensitivity": "css", + "endOfLine": "lf" +} diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..cb9106a --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +# Repository CODEOWNERS + +* @krummbar diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..181c30d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# Set the base image to use for subsequent instructions +FROM node:lts-alpine3.20 + +# bash is required for entrypoint.sh +RUN apk add --no-cache bash + +# Install bruno cli +RUN npm install -g @usebruno/cli@1.x + +# Set the working directory inside the container +WORKDIR /usr/src + +# Copy any source file(s) required for the action +COPY entrypoint.sh . + +# Configure the container to be run as an executable +ENTRYPOINT ["/usr/src/entrypoint.sh"] diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..c8f5d6c --- /dev/null +++ b/README.adoc @@ -0,0 +1,260 @@ += Bruno Run Action +// ############################################################ +// ATTENTION! +// ---------- +// Do not edit the README.adoc file. It is generated from the sources +// located in the /docs folder. The root file for the documentation is +// /docs/index.adoc +// ############################################################ +:source-highlighter: highlight.js +:toc: macro +:icons: font +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:important-caption: :heavy_exclamation_mark: +:caution-caption: :fire: +:warning-caption: :warning: +endif::[] +:action-badge-ci: https://github.com/krummbar/bruno-run-action/actions/workflows/ci.yml/badge.svg +:action-badge-lint: https://github.com/krummbar/bruno-run-action/actions/workflows/linter.yml/badge.svg +:bru-cli-version: 1.x +:url-action-ci: https://github.com/krummbar/bruno-run-action/actions/workflows/ci.yml +:url-action-lint: https://github.com/krummbar/bruno-run-action/actions/workflows/linter.yml +:url-bruno-cli: https://docs.usebruno.com/bru-cli/overview +:url-bruno-npm: https://www.npmjs.com/package/@usebruno/cli + +image:{action-badge-ci}[Continuous Integration,link={url-action-ci}] +image:{action-badge-lint}[Lint Codebase,link={url-action-lint}] + +Containerized GitHub Action executing {url-bruno-cli}[bruno cli] runner. +Can be used to execute sanity checks and integration tests via bruno collections. +Internally executes {url-bruno-npm}[`@usebruno/cli@1.x`], passing the action's input parameters to the command line interface. + +toc::[] + +:leveloffset: 1 + += Usage + +The following example runs a collection located in the folder `bruno/api-it`. +It uses the bruno environment `integration`, +and provides the secret environment variables `foo=bar` and `say=hello`. +The runner results are dumped to the JSON file `./output.json`. + +.Run full collection with secrets +[source,yaml] +---- +name: Bruno Test Collection + +on: + workflow_dispatch: + +jobs: + bruno-tests: + name: Bruno test collection + runs-on: ubuntu-latest + steps: + - name: Bruno CLI runner + id: bru-cli + uses: krummbar/bruno-run-action@main # <1> + with: + path: bruno/api-it + env: integration + env-vars: |- # <2> + foo=bar + say='${{ secrets.MESSAGE }}' + output: output.json + output-format: json +---- +<1> Change `@main` to a specific commit SHA or version tag, e.g.: + +`krummbar/bruno-run-action@e76147da8e5c81eaf017dede5645551d4b94427b` + +`krummbar/bruno-run-action@v0.1.0` +<2> envVars need to be separated with line breaks + +.Run single request +[source,yaml] +---- +name: Bruno Run Individual Folder + +on: + workflow_dispatch: + +jobs: + bruno-tests: + name: Bruno test collection + runs-on: ubuntu-latest + steps: + - name: Bruno CLI runner + id: bru-cli + uses: krummbar/bruno-run-action@main + with: + path: bruno/api-it # <1> + filename: echo # <2> + recursive: true # <3> +---- +<1> Path to bruno collection +<2> Relative path of the request folder inside the collection +<3> Includes all requests in subfolders + +== Inputs + +[source,yaml] +---- +csv-file-path: + description: CSV file pat for input parameters + required: false +path: + # prettier-ignore + description: >- + Path of the target bruno collection. + Should point to the root directory of the collection. + If not provided it uses the current working directory. + required: false + default: . +filename: + # prettier-ignore + description: >- + File or folder name of the requests to run. + It is relative to the `path` input. + required: false +recursive: + description: Indicate a recursive run. + required: false + default: 'false' +ca-cert: + description: CA certificate to verify peer against. + required: false +env: + description: Select a collection environment. + required: false + default: '' +env-vars: + description: List of environment variables to set with the run. + required: false + default: '' +ignore-truststore: + # prettier-ignore + description: >- + The specified custom CA certificate (--cacert) will be used exclusively and the default truststore is ignored, + if this option is specified. + Evaluated in combination with "--cacert" only. + required: false + default: 'false' +output: + # prettier-ignore + description: >- + Output file containing test results. + The path is relative to the current working-directory and not to the targeted bruno collection. + required: false +output-format: + # prettier-ignore + description: >- + The output format of the test results. + Possible values `html|json|junit`. + required: false + default: json +insecure: + description: Allow insecure server connections. + required: false + default: 'false' +sandbox: + description: Javscript sandbox to use. Available sandboxes are 'developer|safe' + required: false + default: '' +tests-only: + description: Only run requests that have a test. + required: false +bail: + description: Stop execution after a failure of a request, test, or assertion. + required: false +---- + +== Outputs + +[source,yaml] +---- +success: + description: Indicates test run success status. +bru-version: + description: Bruno CLI version used during workflow execution. +---- + +:leveloffset!: + +== Development + +:leveloffset: 2 + += Test Locally + +After you've cloned the repository to your local machine or codespace, you'll +need to perform some initial setup steps before you can test your action. + +[NOTE] +==== +You'll need to have a reasonably modern version of +https://www.docker.com/get-started/[Docker] handy (e.g. docker engine +version 20 or later). +==== + +. :hammer_and_wrench: Build the container ++ +[source,console] +---- +docker build -t bruno-run-action-local . +---- + +. :white_check_mark: Test the container ++ +You can pass individual environment variables using the `--env` or `-e` flag. ++ +[source,console] +---- +$ docker run --env INPUT_PATH=".github/bruno-collection" --env INPUT_ENV="cicd" --env BRUNO_ACTION_DRY_RUN="true" -v ${PWD}:/usr/src bruno-run-action-local +::notice::collection directory: '/usr/src/.github/bruno-collection' +::notice::bru run --env cicd +::notice::Executed in dry mode, skipped executing bruno collection +---- ++ +Or you can pass a file with environment variables using `--env-file`. ++ +[source,console] +---- +$ docker run --env-file .github/workflows/ci.env -v ${PWD}/.github:/usr/src/.github bruno-run-action-local +::notice::collection directory: '/usr/src/.github/bruno-collection' +::notice::bru run users/get-user.bru -r --env cicd --output /usr/src/output.html --format html --insecure --tests-only --bail --env-var apikey=myPassword --env-var id=myId +::notice::Executed in dry mode, skipped executing bruno collection +---- ++ +[TIP] +==== +If `BRUNO_ACTION_DRY_RUN=true` is provided, +execution of the actual collection is skipped. +Only the fully composed bru run command with all arguments is dumped. +==== + +:leveloffset!: + +:leveloffset: 2 + += Documentation +:url-asciidoctor: https://docs.asciidoctor.org/asciidoc/latest/ +:url-asciidoctor-reducer: https://github.com/asciidoctor/asciidoctor-reducer + +The `README.adoc` file is generated from the sources in the link:docs[docs] folder. +Any documentation changes must be applied to the files located in there. + +Prerequisites:: +* {url-asciidoctor}[AsciiDoc] +* {url-asciidoctor-reducer}[AsciiDoctor Reducer] + +In order to update the contents of the `README.adoc` run the following command. + +.Update README.adoc +[source,console] +---- +asciidoctor-reducer --preserve-conditionals -o README.adoc docs/index.adoc +---- + +:leveloffset!: diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..fc5536e --- /dev/null +++ b/action.yml @@ -0,0 +1,103 @@ +name: bru-run-action +description: Executes Bru CLI against a bruno collection or single request. +author: Max Bauer +branding: + color: orange + icon: chevron-right + +inputs: + # tag::inputs[] + csv-file-path: + description: CSV file pat for input parameters + required: false + path: + # prettier-ignore + description: >- + Path of the target bruno collection. + Should point to the root directory of the collection. + If not provided it uses the current working directory. + required: false + default: . + filename: + # prettier-ignore + description: >- + File or folder name of the requests to run. + It is relative to the `path` input. + required: false + recursive: + description: Indicate a recursive run. + required: false + default: 'false' + ca-cert: + description: CA certificate to verify peer against. + required: false + env: + description: Select a collection environment. + required: false + default: '' + env-vars: + description: List of environment variables to set with the run. + required: false + default: '' + ignore-truststore: + # prettier-ignore + description: >- + The specified custom CA certificate (--cacert) will be used exclusively and the default truststore is ignored, + if this option is specified. + Evaluated in combination with "--cacert" only. + required: false + default: 'false' + output: + # prettier-ignore + description: >- + Output file containing test results. + The path is relative to the current working-directory and not to the targeted bruno collection. + required: false + output-format: + # prettier-ignore + description: >- + The output format of the test results. + Possible values `html|json|junit`. + required: false + default: json + insecure: + description: Allow insecure server connections. + required: false + default: 'false' + sandbox: + description: Javscript sandbox to use. Available sandboxes are 'developer|safe' + required: false + tests-only: + description: Only run requests that have a test. + required: false + bail: + description: Stop execution after a failure of a request, test, or assertion. + required: false + # end::inputs[] + +outputs: + # tag::outputs[] + success: + description: Indicates test run success status. + bru-version: + description: Bruno CLI version used during workflow execution. + # end::outputs[] + +runs: + using: docker + image: Dockerfile + env: + IN_CSV_FILEPATH: ${{ inputs.csv-file-path }} + IN_PATH: ${{ inputs.path }} + IN_FILENAME: ${{ inputs.filename }} + IN_IGNORE_TRUSTSTORE: ${{ inputs.ignore-truststore }} + IN_RECURSIVE: ${{ inputs.recursive }} + IN_CA_CERT: ${{ inputs.ca-cert }} + IN_ENV: ${{ inputs.env }} + IN_ENV_VARS: ${{ inputs.env-vars }} + IN_OUTPUT: ${{ inputs.output }} + IN_OUTPUT_FORMAT: ${{ inputs.output-format }} + IN_SANDBOX: ${{ inputs.sandbox }} + IN_INSECURE: ${{ inputs.insecure }} + IN_TESTS_ONLY: ${{ inputs.tests-only }} + IN_BAIL: ${{ inputs.bail }} diff --git a/docs/documentation.adoc b/docs/documentation.adoc new file mode 100644 index 0000000..6ec6cdb --- /dev/null +++ b/docs/documentation.adoc @@ -0,0 +1,18 @@ += Documentation +:url-asciidoctor: https://docs.asciidoctor.org/asciidoc/latest/ +:url-asciidoctor-reducer: https://github.com/asciidoctor/asciidoctor-reducer + +The `README.adoc` file is generated from the sources in the link:docs[docs] folder. +Any documentation changes must be applied to the files located in there. + +Prerequisites:: +* {url-asciidoctor}[AsciiDoc] +* {url-asciidoctor-reducer}[AsciiDoctor Reducer] + +In order to update the contents of the `README.adoc` run the following command. + +.Update README.adoc +[source,console] +---- +asciidoctor-reducer --preserve-conditionals -o README.adoc docs/index.adoc +---- diff --git a/docs/edit-advise.adoc b/docs/edit-advise.adoc new file mode 100644 index 0000000..22a18ea --- /dev/null +++ b/docs/edit-advise.adoc @@ -0,0 +1,7 @@ +// ############################################################ +// ATTENTION! +// ---------- +// Do not edit the README.adoc file. It is generated from the sources +// located in the /docs folder. The root file for the documentation is +// /docs/index.adoc +// ############################################################ diff --git a/docs/examples/usage-single-folder.yaml b/docs/examples/usage-single-folder.yaml new file mode 100644 index 0000000..1f03019 --- /dev/null +++ b/docs/examples/usage-single-folder.yaml @@ -0,0 +1,17 @@ +name: Bruno Run Individual Folder + +on: + workflow_dispatch: + +jobs: + bruno-tests: + name: Bruno test collection + runs-on: ubuntu-latest + steps: + - name: Bruno CLI runner + id: bru-cli + uses: krummbar/bruno-run-action@main + with: + path: bruno/api-it # <1> + filename: echo # <2> + recursive: true # <3> diff --git a/docs/examples/usage.yaml b/docs/examples/usage.yaml new file mode 100644 index 0000000..967c06a --- /dev/null +++ b/docs/examples/usage.yaml @@ -0,0 +1,21 @@ +name: Bruno Test Collection + +on: + workflow_dispatch: + +jobs: + bruno-tests: + name: Bruno test collection + runs-on: ubuntu-latest + steps: + - name: Bruno CLI runner + id: bru-cli + uses: krummbar/bruno-run-action@main # <1> + with: + path: bruno/api-it + env: integration + env-vars: |- # <2> + foo=bar + say='${{ secrets.MESSAGE }}' + output: output.json + output-format: json diff --git a/docs/index.adoc b/docs/index.adoc new file mode 100644 index 0000000..5f0dffa --- /dev/null +++ b/docs/index.adoc @@ -0,0 +1,36 @@ += Bruno Run Action +include::edit-advise.adoc[] +:source-highlighter: highlight.js +:toc: macro +:icons: font +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:important-caption: :heavy_exclamation_mark: +:caution-caption: :fire: +:warning-caption: :warning: +endif::[] +:action-badge-ci: https://github.com/krummbar/bruno-run-action/actions/workflows/ci.yml/badge.svg +:action-badge-lint: https://github.com/krummbar/bruno-run-action/actions/workflows/linter.yml/badge.svg +:bru-cli-version: 1.x +:url-action-ci: https://github.com/krummbar/bruno-run-action/actions/workflows/ci.yml +:url-action-lint: https://github.com/krummbar/bruno-run-action/actions/workflows/linter.yml +:url-bruno-cli: https://docs.usebruno.com/bru-cli/overview +:url-bruno-npm: https://www.npmjs.com/package/@usebruno/cli + +image:{action-badge-ci}[Continuous Integration,link={url-action-ci}] +image:{action-badge-lint}[Lint Codebase,link={url-action-lint}] + +Containerized GitHub Action executing {url-bruno-cli}[bruno cli] runner. +Can be used to execute sanity checks and integration tests via bruno collections. +Internally executes {url-bruno-npm}[`@usebruno/cli@1.x`], passing the action's input parameters to the command line interface. + +toc::[] + +include::usage.adoc[leveloffset=1] + +== Development + +include::test-local.adoc[leveloffset=2] + +include::documentation.adoc[leveloffset=2] diff --git a/docs/test-local.adoc b/docs/test-local.adoc new file mode 100644 index 0000000..9263fe6 --- /dev/null +++ b/docs/test-local.adoc @@ -0,0 +1,47 @@ += Test Locally + +After you've cloned the repository to your local machine or codespace, you'll +need to perform some initial setup steps before you can test your action. + +[NOTE] +==== +You'll need to have a reasonably modern version of +https://www.docker.com/get-started/[Docker] handy (e.g. docker engine +version 20 or later). +==== + +. :hammer_and_wrench: Build the container ++ +[source,console] +---- +docker build -t bruno-run-action-local . +---- + +. :white_check_mark: Test the container ++ +You can pass individual environment variables using the `--env` or `-e` flag. ++ +[source,console] +---- +$ docker run --env INPUT_PATH=".github/bruno-collection" --env INPUT_ENV="cicd" --env BRUNO_ACTION_DRY_RUN="true" -v ${PWD}:/usr/src bruno-run-action-local +::notice::collection directory: '/usr/src/.github/bruno-collection' +::notice::bru run --env cicd +::notice::Executed in dry mode, skipped executing bruno collection +---- ++ +Or you can pass a file with environment variables using `--env-file`. ++ +[source,console] +---- +$ docker run --env-file .github/workflows/ci.env -v ${PWD}/.github:/usr/src/.github bruno-run-action-local +::notice::collection directory: '/usr/src/.github/bruno-collection' +::notice::bru run users/get-user.bru -r --env cicd --output /usr/src/output.html --format html --insecure --tests-only --bail --env-var apikey=myPassword --env-var id=myId +::notice::Executed in dry mode, skipped executing bruno collection +---- ++ +[TIP] +==== +If `BRUNO_ACTION_DRY_RUN=true` is provided, +execution of the actual collection is skipped. +Only the fully composed bru run command with all arguments is dumped. +==== diff --git a/docs/usage.adoc b/docs/usage.adoc new file mode 100644 index 0000000..7dc660d --- /dev/null +++ b/docs/usage.adoc @@ -0,0 +1,39 @@ += Usage + +The following example runs a collection located in the folder `bruno/api-it`. +It uses the bruno environment `integration`, +and provides the secret environment variables `foo=bar` and `say=hello`. +The runner results are dumped to the JSON file `./output.json`. + +.Run full collection with secrets +[source,yaml] +---- +include::examples/usage.yaml[] +---- +<1> Change `@main` to a specific commit SHA or version tag, e.g.: + +`krummbar/bruno-run-action@e76147da8e5c81eaf017dede5645551d4b94427b` + +`krummbar/bruno-run-action@v0.1.0` +<2> envVars need to be separated with line breaks + +.Run single request +[source,yaml] +---- +include::examples/usage-single-folder.yaml[] +---- +<1> Path to bruno collection +<2> Relative path of the request folder inside the collection +<3> Includes all requests in subfolders + +== Inputs + +[source,yaml] +---- +include::../action.yml[tag=inputs,indent=0] +---- + +== Outputs + +[source,yaml] +---- +include::../action.yml[tag=outputs,indent=0] +---- diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..95fd85b --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,154 @@ +#!/bin/bash +dry_run="${BRUNO_ACTION_DRY_RUN}" + +function print_input { + echo "::debug::IN_CSV_FILEPATH='${IN_CSV_FILEPATH}'" + echo "::debug::IN_PATH='${IN_PATH}'" + echo "::debug::IN_FILENAME='${IN_FILENAME}'" + echo "::debug::IN_IGNORE_TRUSTSTORE='${IN_IGNORE_TRUSTSTORE}'" + echo "::debug::IN_RECURSIVE='${IN_RECURSIVE}'" + echo "::debug::IN_ENV='${IN_ENV}'" + echo "::debug::IN_ENV_VARS='${IN_ENV_VARS}'" + echo "::debug::IN_OUTPUT='${IN_OUTPUT}'" + echo "::debug::IN_OUTPUT_FORMAT='${IN_OUTPUT_FORMAT}'" + echo "::debug::IN_CA_CERT='${IN_CA_CERT}'" + echo "::debug::IN_INSECURE='${IN_INSECURE}'" + echo "::debug::IN_SANDBOX='${IN_SANDBOX}'" + echo "::debug::IN_TESTS_ONLY='${IN_TESTS_ONLY}'" + echo "::debug::IN_BAIL='${IN_BAIL}'" + echo "::debug::DRY_RUN='${dry_run}'" +} + +# Exit script with status code and message +# +# $1 - Exit code +# $2 - Message to be dumped before exiting +function exit_with { + prefix="::notice" + if [[ "${1}" != "0" ]]; then + prefix="::error" + fi + echo "${prefix}::$2" + exit "$1" +} + +# Takes absolute or relative paths and returns the absolute path value +# based on the current working directory. +# +# $1 - The path of the file that should be resolved as absolute path +# +# Examples (assuming pwd=/home/user/bruno-run-action) +# absolute_path "out/bruno.log" => /home/user/bruno-run-action/out/bruno.log +# absolute_path "/out/bruno.log" => /out/bruno.log +function absolute_path { + input_path="${1}" + origin_dir=$(pwd) + cd "$(dirname "${input_path}")" &>/dev/null || exit_with 1 "The directory of the provided path '${input_path}' does not exist." + out_abs_path=$(pwd) + out_filename=$(basename "${input_path}") + cd "${origin_dir}" || exit_with 1 "Something unexpected happened evaluating the absolute path of '${input_path}'." + echo "${out_abs_path}/${out_filename}" +} + +# Reads all action input parameters and converts them to bru CLI command arguments. +# +# Returns all CLI arguments as string +function parse_bru_args { + output_args="" + if [ -n "${IN_FILENAME}" ]; then + output_args="${IN_FILENAME}" + fi + + if [ -n "${IN_RECURSIVE}" ]; then + output_args="${output_args} -r" + fi + + if [ -n "${IN_ENV}" ]; then + output_args="${output_args} --env ${IN_ENV}" + fi + + if [ "${IN_IGNORE_TRUSTSTORE}" == "true" ]; then + output_args="${output_args} --ignore-truststore" + fi + + if [ -n "${IN_OUTPUT}" ]; then + output_args="${output_args} --output $(absolute_path "${IN_OUTPUT}")" + fi + + if [ -n "${IN_OUTPUT_FORMAT}" ]; then + output_args="${output_args} --format ${IN_OUTPUT_FORMAT}" + fi + + if [ -n "${IN_CA_CERT}" ]; then + output_args="${output_args} --cacert ${IN_CA_CERT}" + fi + + if [ "${IN_INSECURE}" == "true" ]; then + output_args="${output_args} --insecure" + fi + + if [ -n "${IN_SANDBOX}" ]; then + output_args="${output_args} --sandbox ${IN_SANDBOX}" + fi + + if [ "${IN_TESTS_ONLY}" == "true" ]; then + output_args="${output_args} --tests-only" + fi + + if [ "${IN_BAIL}" == "true" ]; then + output_args="${output_args} --bail" + fi + + if [ -n "${IN_CSV_FILEPATH}" ]; then + output_args="${output_args} --csv-file-path $(absolute_path "${IN_CSV_FILEPATH}")" + fi + + # Assign --env-var key=value if provided + # Key value pairs must be separated by line breaks + if [ -n "${IN_ENV_VARS}" ]; then + while read -r env_var; do + output_args="${output_args} --env-var ${env_var}" + done < <(echo -e "${IN_ENV_VARS}") + fi + echo "${output_args}" +} + +# Main function executing the bru CLI +# +# Exits with 0 if `bru run ...` was successful. +# Otherwise returns the exit code of the `bru run ...` command. +function main { + print_input + bru_version="$(bru --version)" + echo "::notice::bru version: ${bru_version}" + echo "bru-version='${bru_version}'" >>"${GITHUB_OUTPUT}" + bru_args="$(parse_bru_args)" + + # Change to provided working directory + if [ -n "${IN_PATH}" ]; then + cd "${IN_PATH}" || exit_with 1 "The provided bruno collection path '${IN_PATH}' does not exist." + fi + + # Dump current working directory + echo "::notice::collection directory: '$(pwd)'" + + # Only dump command if DRY_RUN is enabled and exit + if [ "${dry_run}" = true ]; then + echo "::notice::bru run ${bru_args}" + exit_with 0 "Executed in dry mode, skipped executing bruno collection." + fi + + # Execute 'bru run ...' and evaluate execution status + if eval "bru run ${bru_args}"; then + # Write outputs to the $GITHUB_OUTPUT file + echo "success=true" >>"${GITHUB_OUTPUT}" + exit_with 0 "bru run succeeded." + else + bru_exit_code=$? + # Write outputs to warning $GITHUB_OUTPUT file + echo "success=false" >>"${GITHUB_OUTPUT}" + exit_with ${bru_exit_code} "bru run failed failed with status: ${bru_exit_code}." + fi +} + +main