Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add actions and workflows for QIT tests #81

Merged
merged 21 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
90e1a23
Add actions and workflows for QIT tests
tomalec Sep 27, 2023
764e2d7
Use the pre-released `run-qit-annotate` action.
tomalec Sep 27, 2023
ad7a5fa
Prefix results artifact to avoid conflict with build
tomalec Sep 27, 2023
d8f0673
Forward options input
tomalec Sep 27, 2023
15d3e40
Support local builds
tomalec Sep 27, 2023
cfb8348
Run all QIT tests only on demand
tomalec Sep 27, 2023
a8f23ba
Fix indentation in `run-qit-annotate/README`
tomalec Oct 2, 2023
d7e552e
Transform the `run-qit-extension` workflow into an action.
eason9487 Oct 3, 2023
38a0364
Use JSON format of qit command
tomalec Jan 29, 2024
265e00f
Add wait input to qit action
tomalec Jan 29, 2024
af42247
Merge branch 'add/qit-workflows-alternative' into add/qit-workflows
tomalec Jan 29, 2024
94bd179
Merge branch 'trunk' into add/qit-workflows
tomalec Jan 29, 2024
e93d3c6
Fail GHA when QIT fails (use ignore-fail param if needed)
tomalec Jan 29, 2024
44409f5
Remove results dir from QIT-extensions, add wait param,
tomalec Jan 29, 2024
5bdc33d
Update QIT workflow to use `qit-extension` action
tomalec Jan 29, 2024
5cf8761
Make qit action bash permisive to failures, so we can annotate them
tomalec Jan 30, 2024
f09dda3
Forward qit exitcode to github action
tomalec Jan 30, 2024
c592289
Forward qit exitcode to github action
tomalec Jan 30, 2024
c0a8ee1
Forward ignore fail to qit-extension
tomalec Jan 31, 2024
5f318fb
Add `run-qit-extension` to the actions README
tomalec Feb 6, 2024
d252f0e
Use `qit_token` instead of `application_password`
tomalec Feb 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/run-qit-all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Run QIT for all extensions

# **What it does**: Runs a suite of tests for all Grow extensions.
# **Why we have it**: To be able to run tests for all extensions at once. For example when we want to compatibility test a new version of the platform (WP/WC).

on:
workflow_dispatch:
inputs:
# Basic params.
version:
description: 'Version to be tested'
required: true
type: choice
options:
- latest
- dev

# Configure which tests to run.
test-activation:
description: 'Should activation be tested?'
required: true
default: true
type: boolean
test-security:
description: 'Should security be tested?'
required: true
default: true
type: boolean
test-phpstan:
description: 'Should phpstan be tested?'
required: true
default: true
type: boolean
test-api:
description: 'Should API be tested?'
required: true
default: true
type: boolean
test-e2e:
description: 'Should E2E be tested? (takes a lot of time)'
required: true
default: false
type: boolean

# Advanced customization.
options:
description: 'Additional options for `qit` command, like `--optional_features=hpos`.'
required: false

jobs:
qit-tests:
name: Run QIT Tests
uses: ./.github/workflows/run-qit-extension.yml
secrets: inherit
strategy:
# Allow to test extensions even if one of them fails.
fail-fast: false
matrix:
# List of extensions to be tested.
extension: [automatewoo, automatewoo-birthdays, automatewoo-referrals, google-listings-and-ads, woocommerce-google-analytics-integration]
with:
# Conditional statements are here to allow testing on push triggers, without manual input. To be removed before merging.
version: ${{ inputs.version }}
test-activation: ${{ inputs.test-activation }}
test-security: ${{ inputs.test-security }}
test-phpstan: ${{ inputs.test-phpstan }}
test-api: ${{ inputs.test-api }}
test-e2e: ${{ inputs.test-e2e }}
extension: ${{ matrix.extension }}
options: ${{ inputs.options }}
154 changes: 154 additions & 0 deletions .github/workflows/run-qit-extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
name: Run QIT for a given extensions

# **What it does**: Runs a set of QIT tests for a given extension.
# **Why we have it**: To reuse across other repos, to make a full test of a single extension.

on:
workflow_call:
inputs:
# Basic params.
extension:
description: Extension to test
required: true
type: string
version:
description: |
Version to be tested: `latest`, `dev`, or `local`.
`latest` will test latest released version,
`dev` will make the action look up for an `{extension}.zip` file in the root
of the repository under the `gha-dev-build` tag,
`local` will look up for an `{extension}.zip` artifact in the current workflow.
required: true
default: 'latest'
type: string

# Customize which types to run.
test-activation:
description: 'Should activation be tested?'
default: true
type: boolean
test-security:
description: 'Should security be tested?'
default: true
type: boolean
test-phpstan:
description: 'Should PHPStan be tested?'
default: true
type: boolean
test-api:
description: 'Should API be tested?'
default: true
type: boolean
test-e2e:
description: 'Should E2E be tested?'
default: true
type: boolean

# Advanced customization.
options:
description: 'Additional options for `qit` command, like `--optional_features=hpos`'
type: string

outputs:
statuses:
description: "Statuses of all tests. Array of integers."
value: ${{ jobs.qit-tests.outputs.statuses }}


jobs:
qit-tests:
name: Run QIT Tests
runs-on: ubuntu-20.04
env:
QIT_DISABLE_ONBOARDING: yes
dev_build: ${{ inputs.version != 'latest' && format('{0}.zip', inputs.extension) || '' }}
outputs:
statuses: ${{ toJSON( steps.*.outputs.status ) }}
steps:

- name: Download `gha-dev-build`
if: ${{ inputs.version == 'dev' }}
uses: robinraju/release-downloader@v1.8
with:
repository: "woocommerce/${{ inputs.extension }}"
tag: 'gha-dev-build'
fileName: ${{ env.dev_build }}
token: ${{ secrets.BOT_GH_TOKEN }}

- name: Download artifact
if: ${{ inputs.version == 'local' }}
uses: actions/download-artifact@v3
with:
name: ${{ env.dev_build }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I guess it right, this step is meant to be used in a way like this line?

A concern is the workflow files in .github/workflows directory of this repo won't be included in the actions-v1 releases. As a result, it would become difficult to ensure version compatibility between them.

To run QIT tests with a local build, I would suggest leaving the processing of preparing a local extension zip file on the action user side. For example, GLA could either build a zip file within its workflow or run the actions/download-artifact step on its own, and this action only needs the local file path as input.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is the way to use it.

A concern is the workflow files in .github/workflows directory of this repo won't be included in the actions-v1 releases. As a result, it would become difficult to ensure version compatibility between them.

I completely agree with that concern. That's why in the OP I mentioned:

I was thinking of pushing it to github-actions folder. So, we will have it under common release flow and location, with more convenient version tags. But I'm not sure, as this is "workflow" not "action". Maybe we can store it in a location like .github/workflows/reusable or packages/workflows and preserve that folder in the github-actions release?

I would appreciate your opinion here. I'd say having separate release flows for workflows and separate for actions is too much. I would choose one of those approaches:

  • Consider our [github] actions tag actually mean "github actions & workflows", then
    1. consider workflows and atomic actions completely equal, and make consumers distinguish what is what:
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
         - name: Reuse action
           uses: woocommerce/grow/prepare-php@actions-v1 # Action - should be used in `jobs.*.steps.uses`
      runQIT:
        name: Reuse workflow
        uses: woocommerce/grow/run-qit-extension.yml@actions-v1 # Workflow - should be used in `jobs.*.uses`
        needs: build
    1. put workflows and actions into distinct folders (so maintainers and consumers can easily distinguish what is what)
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
         - name: Reuse action
           uses: woocommerce/grow/action/prepare-php@actions-v1
      runQIT:
        name: Reuse workflow
        uses: woocommerce/grow/workflow/run-qit-extension.yml@actions-v1
        needs: build

To run QIT tests with a local build, I would suggest leaving the processing of preparing a local extension zip file on the action user side. For example, GLA could either build a zip file within its workflow or run the actions/download-artifact step on its own, and this action only needs the local file path as input.

The problem here is that. This is a workflow (jobs.*.uses), not an atomic action (jobs.*.steps.uses). This workflow runs as a separate job, from any other workflow or action. So the only way to share a file (I'm aware of) between this workflow and another (GLA's, or other extension's) workflow is to use artifacts. Then this workflow needs to download them itself. Otherwise, how would GLA's job push a file to this job?

Copy link
Member

@eason9487 eason9487 Oct 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version compatibility concern in #81 (comment) is the same as this thread.

The current way of transferring a local extension zip might not be easy to handle in the future in terms of version compatibility. Wonder if it would be better to leave the processing to the action user side.

Could you elaborate on that?

Sorry, I omitted a large portion of explanation. The idea is to transform the run-qit-extension.yml workflow into a composite action and it involves a degree of change that is not quite easily stated in words. I created a demo in the repo and one in GLA to illustrate how it works.

Changes in this repo

  • Based on the run-qit-extension workflow, create a composite run-qit-extension action.
  • Remove the "Download artifact" step.
  • Add qit-partner-user and qit-partner-secret inputs to forward the original secret variables.
  • Remove all timeout-minutes as they are not supported in the composite action. Probably it would be better to let the action user specify the timeout.
  • I didn't transform the outputs.statuses as it would need more time or the composite action might not support the same syntax.

Changes in GLA

  • Add the "Download artifact" step.
  • Use the run-qit-extension action instead.

The test run: https://github.com/woocommerce/google-listings-and-ads/actions/runs/6391261008

I didn't continue to make further changes. If adopt this method, the .github/workflows/run-qit-all.yml workflow should be able to use run-qit-extension action as well, and the .github/workflows/run-qit-extension.yml workflow would be no longer in needed.

In this way, the action user sides won't need to use this repo's workflow directly, and this repo doesn't need to add them to any release and versioning flow.

Another advantage is that the action user side wouldn't have to upload artifacts if their action builds a local zip file and uses this action in the same job. For example:

# ... omitted

jobs:
  qit-tests:
    name: Run QIT Tests
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      # ... omitted

      - name: Build production bundle
        run: |
          npm run build

      - name: Delegate QIT Tests
        uses: woocommerce/grow/run-qit-extension@actions-v1
        with:
          qit-partner-user: ${{ secrets.QIT_PARTNER_USER }}
          qit-partner-secret: ${{ secrets.QIT_PARTNER_SECRET }}
          version: 'local'
          extension: 'google-listings-and-ads'
          test-security: 'true'

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the detailed explanations and the new branch!
I can see it works, and I appreciate how it addresses the following:

this repo doesn't need to add them [workflows] to any release and versioning flow.

However, I'm not sure if I see all the benefits of

the action user sides won't need to use this repo's workflow directly

  • I get that accessing the workflow directly is not great, and I'm happy to improve it. But I think we can to it also otherwise
  • Using action or workflow from code perspective is not much longer

The biggest difference I see is granularity. Having qit as an atomic action, users could compose it within their workflows.
But I think it also has the downsides:

  • the log is less collapsible and divided.:
    as a workflow:
    image
    as an action
    image

  • I was actually thinking of unifying the workflow across our repos so we will not have to implement a workflow and compose actions in every repo but just use the shared entire workflow.

Especially if it comes to the build, I think it is even desirable to have it as a separate workflow that uploads an artifact. So, other workflows and actions running for the same commit could reuse that work, reducing the overall cost and time.
See woocommerce/google-listings-and-ads#2114 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I was actually thinking of unifying the workflow across our repos so we will not have to implement a workflow and compose actions in every repo but just use the shared entire workflow.

From the perspective of this repo, I would suppose its granularity prefers a custom action over a reusable workflow for flexibility in composition and configuration and fewer limitations.

If an automation requirement is highly repetitive or identical across several repos, then a reusable workflow, which might still composite some custom actions within this repo, would be appropriate.

Especially if it comes to the build, I think it is even desirable to have it as a separate workflow that uploads an artifact. So, other workflows and actions running for the same commit could reuse that work, reducing the overall cost and time. [...]

From a practicable point of view, I find it rather difficult to imagine whether it would be easier to do so.

Take GLA as an example, given it has a reusable workflow for building a zip bundle, and each automation of "Bundle watch", "Publish dev build", "E2E test", and "QIT" need a build file as input.

Unless all these automations are in the same workflow, each automation will be grouped into standalone workflows with the same execution conditions, and then build a zip on its own. Because artifact access through actions/upload-artifact and actions/download-artifact is limited within the same workflow run. In this case, the same commit doesn't use the "same" work result (zip bundle), only the extracted build workflow is reused.

However, having all of them in the same workflow run seems to make that workflow configuration very complex.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main concern is still how the release versions of these reusable workflows in the .github/workflows directory will proceed if go with the current solution.

The unintuitive understanding of the artifact upload/download split between the use-end repo and this repo is also a concern, but if this complexity trade-off is necessary, then it won't be a blocker.

I'd say having separate release flows for workflows and separate for actions is too much.

Agree with this.

I would choose one of those approaches: [...]

GitHub docs say the syntax of jobs.<job_id>.uses is {owner}/{repo}/.github/workflows/{filename}@{ref} and doesn't clarify if the .github/workflows/ part can be changed.

Either yes or no, I lean toward leaving reusable workflows under the original directory and keeping custom actions the same as it's:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
     - name: Reuse action
       uses: woocommerce/grow/prepare-php@actions-v1

  runQIT:
    name: Reuse workflow
    uses: woocommerce/grow/.github/workflows/run-qit-extension.yml@actions-v1
    needs: build

The main reason is that each of them conforms to the syntax described in GitHub docs respectively.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merged your proposed changes and used action as you suggested.
We can get back to shared workflows if needed in some following PRs, exploring possibilities and flow optimizations there.


- name: Install QIT via composer
run: composer require woocommerce/qit-cli
- name: Add Partner
run: |
./vendor/bin/qit partner:add \
--user='${{ secrets.QIT_PARTNER_USER }}' \
--application_password='${{ secrets.QIT_PARTNER_SECRET }}'

- name: Create results dir
run: mkdir -p ./qit-results/${{ inputs.extension }}

- name: Activation test
id: activation-test
if: ${{ inputs.test-activation == true }}
uses: woocommerce/grow/run-qit-annotate@actions-v1.10.2-pre
timeout-minutes: 5
with:
type: activation
extension: ${{ inputs.extension }}
extension-file: ${{ env.dev_build }}
options: ${{ inputs.options }}

- name: Security test
id: security-test
if: ${{ inputs.test-security == true }}
uses: woocommerce/grow/run-qit-annotate@actions-v1.10.2-pre
timeout-minutes: 5
with:
type: security
extension: ${{ inputs.extension }}
extension-file: ${{ env.dev_build }}
options: ${{ inputs.options }}

- name: PHPStan test
id: phpstan-test
if: ${{ inputs.test-phpstan == true }}
uses: woocommerce/grow/run-qit-annotate@actions-v1.10.2-pre
timeout-minutes: 5
with:
type: phpstan
extension: ${{ inputs.extension }}
extension-file: ${{ env.dev_build }}
options: ${{ inputs.options }}

- name: API test
id: api-test
if: ${{ inputs.test-api == true }}
uses: woocommerce/grow/run-qit-annotate@actions-v1.10.2-pre
timeout-minutes: 5
with:
type: api
extension: ${{ inputs.extension }}
extension-file: ${{ env.dev_build }}
options: ${{ inputs.options }}

- name: E2E test
id: e2e-test
if: ${{ inputs.test-e2e == true }}
uses: woocommerce/grow/run-qit-annotate@actions-v1.10.2-pre
timeout-minutes: 30
with:
type: e2e
extension: ${{ inputs.extension }}
extension-file: ${{ env.dev_build }}
options: ${{ inputs.options }}

- name: Upload results
uses: actions/upload-artifact@v1
with:
name: "qit-results-${{ inputs.extension }}"
path: ./qit-results/${{ inputs.extension }}
1 change: 1 addition & 0 deletions packages/github-actions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Custom GitHub actions that help to composite GitHub workflows across the repos m
- [`prepare-node`](actions/prepare-node) - Set up Node.js with a specific version, load npm cache, install Node dependencies
- [`prepare-php`](actions/prepare-php) - Set up PHP with a specific version and tools, load Composer cache, install Composer dependencies
- [`publish-extension-dev-build`](actions/publish-extension-dev-build) - Publish extension development build
- [`run-qit-annotate`](actions/run-qit-annotate) - Runs QIT test and annotates the results
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The run-qit-extension action could be listed here as well.

- [`stylelint-annotation`](actions/stylelint-annotation) - Annotate stylelint results via stylelint formatter
- [`update-version-tags`](actions/update-version-tags) - Update version tags

Expand Down
55 changes: 55 additions & 0 deletions packages/github-actions/actions/run-qit-annotate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Run QIT test

This action provides the following functionality for GitHub Actions users:

- Run `qit` test of given type for a given extension
- Annotate the results
- Forward status code, so the consumer can decide how to conclude the results


## Usage

See [action.yml](action.yml)

### Prerequisites

- QIT needs to be [installed](https://woocommerce.github.io/qit-documentation/#/cli/getting-started?id=installing-qit) and [authenticated](https://woocommerce.github.io/qit-documentation/#/authenticating?id=cli)
- To setup QIT, you need to set `QIT_DISABLE_ONBOARDING` env to `yes`.
- The action assumes there is `qit-results` directory. You can change it's name using `results-folder` input.


### Basic:

```yaml
jobs:
qit-test:
name: Run QIT Tests
runs-on: ubuntu-20.04
env:
QIT_DISABLE_ONBOARDING: yes
steps:
- name: Install QIT via composer
run: composer require woocommerce/qit-cli

- name: Add Partner
run: |
./vendor/bin/qit partner:add \
--user='${{ secrets.QIT_PARTNER_USER }}' \
--application_password='${{ secrets.QIT_PARTNER_SECRET }}'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Noticed this deprecation message on the GL&A side. Looks like this could be changed to use qit_token.


- name: Create results dir
run: mkdir -p ./qit-results/automatewoo

- name: Security test
id: security-test
uses: woocommerce/grow/run-qit-annotate@actions-v1
timeout-minutes: 5
with:
type: security
extension: automatewoo
options: '--optional_features=hpos'

- name: Echo status
shell: bash
run: echo ${{ jobs.security-test.outputs.status }}
```
94 changes: 94 additions & 0 deletions packages/github-actions/actions/run-qit-annotate/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Run QIT test
description: Runs QIT test and annotates the results.

# **What it does**: Runs a single QIT test and annotates the results.
# **Why we have it**: To reuse across other workflows to test extensions.

inputs:
# Basic params.
extension:
description: Extension to test
required: true
extension-file:
description: Custom build of the extension to test. If not given, the latest release will be used.
required: false
type:
description: Type of test to run
required: false
default: activation
# Advanced customization.
options:
description: Options to pass to the qit command
required: false
default: ''
results-filename:
description: Custom file name for results
required: false
results-folder:
description: Folder to store results
required: false
default: qit-results

outputs:
status:
description: "Exit code of the test. May be used, for example, to fail the workflow if the test fails."
value: ${{ steps.run-test.outputs.status }}
summary:
description: "Short summary of the test"
value: ${{ steps.read-summary.outputs.summary }}
resultsURL:
description: "URL to the results of the test"
value: ${{ steps.read-summary.outputs.resultURL }}

branding:
icon: 'award'
color: 'purple'

runs:
using: composite
steps:
- name: Run test
id: run-test
# Do not fail when the `qit` fails, so we can annotate the results.
shell: bash --noprofile --norc {0}
continue-on-error: true
env:
# If the custom build is provided, pass it to the `zip` param.
zip: ${{ inputs.extension-file && format('--zip={0}', inputs.extension-file) || '' }}
report_file: ${{ inputs.results-filename || format( '{0}/{1}/{2}/{3}.txt', github.workspace, inputs.results-folder, inputs.extension, inputs.type ) }}
status: 0
run: |
./vendor/bin/qit run:${{ inputs.type }} ${{ inputs.extension }} \
$zip \
${{ inputs.options }} \
--wait \
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
> $report_file || status=$?
echo "status=$status" >> "$GITHUB_OUTPUT"
echo "report_file=$report_file" >> "$GITHUB_OUTPUT"
cat $report_file

# Parse the report file, to fetch the essential information.
- name: Read summary
id: read-summary
shell: bash
if: '!cancelled()'
run: |
summary=`grep -Po "(?<=Test Summary)\s+(.*)" ${{ steps.run-test.outputs.report_file }} --color=never`
resultURL=`grep -Po "(?<=Result Url)\s+(.*)" ${{ steps.run-test.outputs.report_file }} --color=never`
echo "summary=$summary" >> $GITHUB_OUTPUT
echo "resultURL=$resultURL" >> $GITHUB_OUTPUT

# Annotate the results according to the exit code.
- name: Annotate
if: '!cancelled()'
shell: bash
run: |
summary="${{ inputs.type }}: ${{ steps.read-summary.outputs.summary }} - ${{ steps.read-summary.outputs.resultURL }}";
case ${{ steps.run-test.outputs.status }} in
0) echo "::notice ::$summary"
;;
2) echo "::warning ::$summary"
;;
*) echo "::error ::$summary"
;;
esac