diff --git a/.copywrite.hcl b/.copywrite.hcl index 1be2d22001..dac6e0ced1 100644 --- a/.copywrite.hcl +++ b/.copywrite.hcl @@ -2,21 +2,25 @@ schema_version = 1 project { license = "BUSL-1.1" - copyright_year = 2025 + copyright_year = 2016 copyright_holder = "IBM Corp." + ignore_year1 = true # (OPTIONAL) A list of globs that should not have copyright/license headers. # Supports doublestar glob patterns for more flexibility in defining which # files or folders should be ignored header_ignore = [ + ".git/**", + "enos/.enos/**", + "enos/.terraform/**", + "enos/k8s/.enos/**", + "enos/modules/k8s_deploy_vault/raft-config.hcl", + "enos/modules/zap_scan_ent/templates/plan.yml", "helper/pkcs7/**", + "plugins/database/postgresql/scram/**", + "tools/pipeline/internal/pkg/generate/fixtures/*", "ui/node_modules/**", "ui/pnpm-lock.yaml", "ui/pnpm-workspace.yaml", - "enos/modules/k8s_deploy_vault/raft-config.hcl", - "plugins/database/postgresql/scram/**", - "enos/.enos/**", - "enos/k8s/.enos/**", - "enos/.terraform/**", ] } diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..6b74c047ca --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git +enos diff --git a/.github/actionlint.yml b/.github/actionlint.yml index 8385af154f..11f5016522 100644 --- a/.github/actionlint.yml +++ b/.github/actionlint.yml @@ -1,4 +1,4 @@ -# Copyright IBM Corp. 2016, 2025 +# Copyright IBM Corp. 2016, 2026 # SPDX-License-Identifier: BUSL-1.1 self-hosted-runner: @@ -8,14 +8,9 @@ self-hosted-runner: - medium - large - xlarge - - ondemand - - disk_gb=64 - - os=linux - - os=ubuntu-arm - - type=m5.2xlarge - - type=c6a.xlarge - - type=c6g.2xlarge - - type=c6a.4xlarge - - type=c6g.2xlarge;c6g.4xlarge - - ubuntu-latest-x64 + - ubuntu-22.04-x64 + - ubuntu-22.04-x64 + - ubuntu-24.04-arm64 - custom-linux-xl-vault-latest + - stats + - type=m6a.8xlarge diff --git a/.github/actions/build-ui/action.yml b/.github/actions/build-ui/action.yml new file mode 100644 index 0000000000..973401a799 --- /dev/null +++ b/.github/actions/build-ui/action.yml @@ -0,0 +1,57 @@ +# Copyright IBM Corp. 2016, 2026 +# SPDX-License-Identifier: BUSL-1.1 + +--- +name: Build UI +description: | + Build the Vault Web UI assets and cache the results. + +inputs: + github-token: + description: An elevated Github token to access private dependencies if necessary. + default: "" + +outputs: + cache-key: + description: "The cache key for the built UI assets (format: ui-)" + value: ${{ steps.ui-hash.outputs.cache-key }} + cache-hit: + description: "Whether the UI was restored from cache (true) or built fresh (false)" + value: ${{ steps.cache-ui-assets.outputs.cache-hit }} + +runs: + using: composite + steps: + - name: Get UI hash + id: ui-hash + shell: bash + run: | + ui_hash=$(git ls-tree HEAD ui --object-only) + echo "cache-key=ui-${ui_hash}" | tee -a "$GITHUB_OUTPUT" + - name: Set up UI asset cache + id: cache-ui-assets + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + with: + enableCrossOsArchive: true + lookup-only: true + path: http/web_ui + # Only restore the UI asset cache if we haven't modified anything in the ui directory. + # Never do a partial restore of the web_ui if we don't get a cache hit. + key: ${{ steps.ui-hash.outputs.cache-key }} + - name: Install PNPM + if: ${{ steps.cache-ui-assets.outputs.cache-hit != 'true' }} + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 + with: + run_install: false + package_json_file: './ui/package.json' + - name: Set up node and pnpm + if: ${{ steps.cache-ui-assets.outputs.cache-hit != 'true' }} + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: ui/package.json + cache: pnpm + cache-dependency-path: ui/pnpm-lock.yaml + - name: Build UI + if: ${{ steps.cache-ui-assets.outputs.cache-hit != 'true' }} + shell: bash + run: make ci-build-ui diff --git a/.github/actions/build-vault/action.yml b/.github/actions/build-vault/action.yml index 616631b282..b72cd2fd97 100644 --- a/.github/actions/build-vault/action.yml +++ b/.github/actions/build-vault/action.yml @@ -69,7 +69,7 @@ runs: shell: bash run: git config --global url."https://${{ inputs.github-token }}:@github.com".insteadOf "https://github.com" - name: Restore UI from cache - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: # Restore the UI asset from the UI build workflow. Never use a partial restore key. enableCrossOsArchive: true @@ -117,7 +117,7 @@ runs: id: build-vault-select-license uses: hashicorp-forge/actions-pao-tool/select-license@6997f7457c338e008506005cc370e7b02f7fb421 # v1.0.3 with: - arch: ${{ matrix.goarch }} + arch: ${{ inputs.goarch }} - if: inputs.cgo-enabled == '0' name: ${{ steps.metadata.outputs.build-step-name }} env: @@ -133,7 +133,7 @@ runs: shell: bash run: make ci-build - if: inputs.cgo-enabled == '1' - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 with: driver-opts: network=host # So we can run our own little registry - if: inputs.cgo-enabled == '1' @@ -143,7 +143,7 @@ runs: if: inputs.cgo-enabled == '1' id: build-push-action-attempt-1 continue-on-error: true # we will retry this if it fails - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 env: DOCKER_BUILD_SUMMARY: false with: @@ -165,7 +165,7 @@ runs: id: build-push-action-attempt-2 continue-on-error: false if: inputs.cgo-enabled == '1' && steps.build-push-action-attempt-1.outcome != 'success' - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 env: DOCKER_BUILD_SUMMARY: false with: @@ -218,12 +218,16 @@ runs: BUNDLE_PATH: out/${{ steps.metadata.outputs.artifact-basename }}.zip shell: bash run: make ci-bundle - - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: ${{ steps.metadata.outputs.artifact-basename }}.zip path: out/${{ steps.metadata.outputs.artifact-basename }}.zip if-no-files-found: error - if: inputs.create-packages == 'true' + env: + # Use our elevated token instead of the workflow token so that our + # download of nfpm is less likely to fail. + GH_TOKEN: ${{ inputs.github-token }} uses: hashicorp/actions-packaging-linux@33f7d23b14f24e6a7b7d9948cb7f5caca2045ee3 with: name: ${{ inputs.package-name }} @@ -250,13 +254,13 @@ runs: echo "deb-files=$(basename out/*.deb)" } | tee -a "$GITHUB_OUTPUT" - if: inputs.create-packages == 'true' - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: ${{ steps.package-files.outputs.rpm-files }} path: out/${{ steps.package-files.outputs.rpm-files }} if-no-files-found: error - if: inputs.create-packages == 'true' - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: ${{ steps.package-files.outputs.deb-files }} path: out/${{ steps.package-files.outputs.deb-files }} diff --git a/.github/actions/create-dynamic-config/action.yml b/.github/actions/create-dynamic-config/action.yml index 4cf11c190e..5bd5fbe951 100644 --- a/.github/actions/create-dynamic-config/action.yml +++ b/.github/actions/create-dynamic-config/action.yml @@ -20,26 +20,21 @@ runs: id: dyn-cfg-metadata shell: bash run: | - # Since the dynamic config is created by a specific version of the - # pipeline tool, we always need to factor it into our cache key. - # If the pipeline changes we always want to make sure we are using - # dynamic config from the latest version. + # The cache key is a short SHA of the enos-dynamic-config.tmpl file. + # We also include a weekly date in the cache key so that we regenerate + # the configuration on at least a weekly basis to account for changing + # versions. If we add more templates to this we ought to consider their + # short sha's in the key too! # - # We use a weekly date in the cache key so that we regenerate the - # configuration on at least a weekly basis - # - # If/when Github decides to purge our tiny config file cache we'll also - # recreate it as necessary. - # - # Uses GITHUB_ENV instead of GITHUB_OUTPUT because composite actions are broken, - # see: https://github.com/actions/cache/issues/803#issuecomment-1793565071 + # Uses GITHUB_ENV instead of GITHUB_OUTPUT because composite actions are + # broken, see: https://github.com/actions/cache/issues/803#issuecomment-1793565071 { - echo "DYNAMIC_CONFIG_KEY=${{ inputs.vault-version }}-$(date +%Y-%m-%U)-$(git ls-tree HEAD tools/pipeline --object-only --abbrev=8)" + echo "DYNAMIC_CONFIG_KEY=${{ inputs.vault-version }}-$(date +%Y-%m-%U)-$(git ls-tree HEAD enos/enos-dynamic-config.tmpl --object-only --abbrev=8)" echo "DYNAMIC_CONFIG_PATH=enos/enos-dynamic-config.hcl" } | tee -a "$GITHUB_ENV" - name: Try to restore dynamic config from cache id: dyn-cfg-cache - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ${{ env.DYNAMIC_CONFIG_PATH }} key: dyn-cfg-${{ env.DYNAMIC_CONFIG_KEY }} @@ -56,9 +51,8 @@ runs: shell: bash run: | # Make sure that any branch specific dynamic config has been generated - pipeline generate enos-dynamic-config ${{ runner.debug && '--log debug' }}\ - -d ./enos \ - --file enos-dynamic-config.hcl \ - -v ${{ inputs.vault-version }} \ - -e ${{ inputs.vault-edition }} \ - -n 3 + pipeline generate template ${{ runner.debug && '--log debug' }} \ + enos/enos-dynamic-config.tmpl \ + enos/enos-dynamic-config.hcl \ + --version '${{ inputs.vault-version }}' \ + --edition '${{ inputs.vault-edition }}' diff --git a/.github/actions/install-tools/action.yml b/.github/actions/install-tools/action.yml index 7274562fa2..3b33e7c716 100644 --- a/.github/actions/install-tools/action.yml +++ b/.github/actions/install-tools/action.yml @@ -69,7 +69,7 @@ runs: echo "VAULT_TOOLS_CACHE_KEY=${cache_key}" } | tee -a "$GITHUB_ENV" - id: cache-tools - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: lookup-only: ${{ inputs.no-restore }} path: ${{ env.VAULT_TOOLS_PATH }} diff --git a/.github/actions/metadata/action.yml b/.github/actions/metadata/action.yml index 8c7efd1ff5..c13c627ac7 100644 --- a/.github/actions/metadata/action.yml +++ b/.github/actions/metadata/action.yml @@ -156,9 +156,11 @@ runs: if [ "$is_enterprise_repo" = 'true' ]; then base_ref='${{ github.event.pull_request.base.ref || github.event.base_ref || github.ref_name || github.event.branch || github.ref }}' is_ent_repo='true' - is_ce_in_enterprise=$([[ $base_ref == ce/* ]] && echo "true" || echo "false") + is_ent_branch='true' + is_ce_in_enterprise=$([[ $base_ref == ce/* || $base_ref == refs/heads/ce/* ]] && echo "true" || echo "false") if [ "$is_ce_in_enterprise" = 'true' ]; then is_enterprise="false" + is_ent_branch="false" go_tags='' version_metadata='${{ inputs.vault-version }}' else @@ -166,10 +168,10 @@ runs: go_tags='ent,enterprise' version_metadata='${{ inputs.vault-version }}+ent' fi - compute_build='["self-hosted","ondemand","os=linux","disk_gb=64","type=c6a.4xlarge;c5a.4xlarge"]' - compute_build_ui='["self-hosted","ondemand","os=linux","disk_gb=64","type=c6a.2xlarge;c5a.2xlarge;c6a.4xlarge"]' - compute_test_go='["self-hosted","ondemand","os=linux","disk_gb=64","type=c6a.2xlarge;c5a.2xlarge;c6a.4xlarge"]' - compute_test_ui='["self-hosted","ondemand","os=linux","type=m6a.2xlarge;m6a.4xlarge"]' + compute_build='["self-hosted","ubuntu-22.04-x64"]' + compute_build_ui='["self-hosted","ubuntu-22.04-x64"]' + compute_test_go='["self-hosted","ubuntu-22.04-x64"]' + compute_test_ui='["self-hosted","ubuntu-22.04-x64"]' compute_small='["self-hosted","linux","small"]' else compute_build='"custom-linux-medium-vault-latest"' @@ -191,7 +193,7 @@ runs: echo "compute-small=${compute_small}" echo "go-tags=${go_tags}" echo "is-ce-in-enterprise=${is_ce_in_enterprise}" - echo "is-ent-branch=${is_enterprise}" + echo "is-ent-branch=${is_ent_branch}" echo "is-ent-repo=${is_ent_repo}" echo "vault-version-metadata=${version_metadata}" } | tee -a "$GITHUB_OUTPUT" diff --git a/.github/actions/run-enos-scenario/README.md b/.github/actions/run-enos-scenario/README.md new file mode 100644 index 0000000000..bdccb77631 --- /dev/null +++ b/.github/actions/run-enos-scenario/README.md @@ -0,0 +1,114 @@ +# run-enos-scenario + +Reusable composite action for running an Enos scenario with standardized retry, debug artifact upload, and destroy cleanup behavior. + +## Behavior + +The action performs this lifecycle: + +1. Optionally installs Terraform. +2. Optionally installs Enos. +3. Prepares a sanitized debug artifact name from the scenario filter. +4. Runs `enos scenario launch`. +5. Retries the launch command once if the first attempt fails. +6. Optionally prints `enos scenario exec --cmd show` and `plan` before retry and after a failed retry. +7. Uploads debug data on failure. +8. Always destroys the scenario and retries destroy once if needed. + +## Inputs + +| Name | Required | Default | Description | +| --- | --- | --- | --- | +| `scenario-filter` | yes | n/a | Scenario filter passed to Enos. | +| `working-directory` | no | `./enos` | Value passed to `--chdir`. | +| `timeout` | no | `45m0s` | Timeout for the first main command attempt. | +| `retry-timeout` | no | `30m0s` | Timeout for the retry attempt. | +| `destroy-timeout` | no | `10m0s` | Timeout for destroy. | +| `show-state-on-retry` | no | `false` | When `true`, prints scenario state and plan before retry and after failed retry. | +| `upload-debug-data` | no | `true` | When `true`, uploads debug data on failure. | +| `debug-data-retention-days` | no | `30` | Artifact retention days for debug data. | +| `debug-data-path` | no | empty | Explicit debug data directory. If empty, uses `ENOS_DEBUG_DATA_ROOT_DIR` or `/tmp/enos-debug-data`. | +| `setup-terraform` | no | `true` | When `true`, installs Terraform. | +| `setup-enos` | no | `true` | When `true`, installs Enos. | +| `launch-extra-args` | no | empty | Extra arguments added to the launch command before the scenario filter. | +| `retry-extra-args` | no | empty | Extra arguments added to the retry launch command before the scenario filter. | +| `destroy-extra-args` | no | empty | Extra arguments added to destroy before the scenario filter. | +| `pre-destroy-command` | no | empty | Optional enos scenario command to run before destroying (e.g., `exec`). | +| `pre-destroy-extra-args` | no | empty | Extra arguments added to the pre-destroy command before the scenario filter. | + +## Outputs + +| Name | Description | +| --- | --- | +| `launch-outcome` | Final main phase outcome: `success` or `failure`. | +| `launch-retry-attempted` | `true` when the main phase retry was attempted. | +| `destroy-outcome` | Final destroy outcome: `success` or `failure`. | +| `destroy-retry-attempted` | `true` when destroy retry was attempted. | +| `debug-data-artifact-name` | Sanitized debug artifact name derived from the scenario filter. | +| `debug-data-dir` | Debug data directory used by the action. | +| `error-message` | Failure message captured from the retry step, when present. | + +## Usage + +### Standard launch workflow + +```yaml +- name: Run Enos scenario + id: run-scenario + uses: ./.github/actions/run-enos-scenario + with: + scenario-filter: ${{ matrix.scenario.id.filter }} + timeout: 45m0s + retry-timeout: 30m0s + destroy-timeout: 10m0s + show-state-on-retry: 'true' +``` + +### With custom destroy arguments + +```yaml +- name: Run Enos scenario + id: run-scenario + uses: ./.github/actions/run-enos-scenario + with: + scenario-filter: ${{ inputs.scenario }} + timeout: 60m0s + retry-timeout: 60m0s + destroy-timeout: 60m0s + destroy-extra-args: --grpc-listen http://localhost +``` + +### Skip tool installation + +```yaml +- name: Run Enos scenario + uses: ./.github/actions/run-enos-scenario + with: + scenario-filter: ${{ steps.sample.outputs.filter }} + setup-terraform: 'false' + setup-enos: 'false' +``` + +### With pre-destroy command + +```yaml +- name: Run Enos scenario + uses: ./.github/actions/run-enos-scenario + with: + scenario-filter: ${{ inputs.scenario }} + timeout: 30m0s + pre-destroy-command: exec + pre-destroy-extra-args: --cmd 'output -raw scan_markdown' +``` + +### With pre-destroy command and output redirection + +```yaml +- name: Run Enos scenario + uses: ./.github/actions/run-enos-scenario + with: + scenario-filter: ${{ inputs.scenario }} + timeout: 30m0s + pre-destroy-command: exec + pre-destroy-extra-args: --cmd 'output -raw scan_markdown' | tee -a "$GITHUB_STEP_SUMMARY" +``` diff --git a/.github/actions/run-enos-scenario/action.yml b/.github/actions/run-enos-scenario/action.yml new file mode 100644 index 0000000000..6c2dfb389c --- /dev/null +++ b/.github/actions/run-enos-scenario/action.yml @@ -0,0 +1,266 @@ +# Copyright IBM Corp. 2016, 2026 +# SPDX-License-Identifier: BUSL-1.1 + +--- +name: Run and retry an Enos scenario +description: | + Run an Enos scenario with a standard launch retry flow, optional state/plan diagnostics, + automatic debug data artifact naming, and destroy cleanup with a destroy retry. + +inputs: + scenario-filter: + description: Enos scenario filter/identifier (for example "ui edition:ent backend:raft") + required: true + working-directory: + description: Working directory to pass to enos via --chdir + required: false + default: ./enos + timeout: + description: Timeout for the initial launch command + required: false + default: 45m0s + retry-timeout: + description: Timeout for the retry launch attempt + required: false + default: 30m0s + destroy-timeout: + description: Timeout for the destroy command + required: false + default: 10m0s + show-state-on-retry: + description: Whether to show scenario state and plan before and after a failed retry (true/false) + required: false + default: 'false' + upload-debug-data: + description: Whether to upload Enos debug data on failure (true/false) + required: false + default: "true" + debug-data-retention-days: + description: Retention period in days for uploaded debug data artifacts + required: false + default: "30" + debug-data-path: + description: Debug data root directory to create and upload + required: false + default: "" + setup-terraform: + description: Whether to install Terraform in the action (true/false) + required: false + default: "true" + setup-enos: + description: Whether to install Enos in the action (true/false) + required: false + default: "true" + launch-extra-args: + description: Extra arguments to append to the launch command before the scenario filter + required: false + default: "" + retry-extra-args: + description: Extra arguments to append to the retry launch command before the scenario filter + required: false + default: "" + destroy-extra-args: + description: Extra arguments to append to the destroy command before the scenario filter + required: false + default: "" + pre-destroy-command: + description: Optional enos scenario command to run before destroying (e.g., "exec") + required: false + default: "" + pre-destroy-extra-args: + description: Extra arguments to append to the pre-destroy command before the scenario filter + required: false + default: "" + +outputs: + launch-outcome: + description: Final execution outcome for the main phase (success or failure) + value: ${{ steps.determine-launch-outcome.outputs.outcome }} + launch-retry-attempted: + description: Whether a retry was attempted for the main phase + value: ${{ steps.determine-launch-outcome.outputs.retry-attempted }} + destroy-outcome: + description: Final destroy outcome (success or failure) + value: ${{ steps.determine-destroy-outcome.outputs.outcome }} + destroy-retry-attempted: + description: Whether a destroy retry was attempted + value: ${{ steps.determine-destroy-outcome.outputs.retry-attempted }} + debug-data-artifact-name: + description: Debug data artifact name prepared by the action + value: ${{ steps.prepare-artifacts.outputs.debug-data-artifact-name }} + debug-data-dir: + description: Debug data directory prepared by the action + value: ${{ steps.prepare-artifacts.outputs.debug-data-dir }} + error-message: + description: Error message captured from a failed retry + value: ${{ steps.launch-retry.outputs.error-message }} + +runs: + using: composite + steps: + - if: inputs.setup-terraform == 'true' + name: Setup Terraform + uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4.0.0 + with: + terraform_wrapper: false + + - if: inputs.setup-enos == 'true' + name: Setup Enos + uses: hashicorp/action-setup-enos@6ec106c8f809fe645162d73bea565c65f3269907 # v1.52 + + - id: prepare-artifacts + name: Prepare artifacts and debug directory + shell: bash + run: | + artifact_name="enos-debug-data_$(echo "${{ inputs.scenario-filter }}" | sed -e 's/ /_/g' | sed -e 's/:/=/g')" + debug_dir="${{ inputs.debug-data-path }}" + if [ -z "${debug_dir}" ]; then + debug_dir="${ENOS_DEBUG_DATA_ROOT_DIR:-/tmp/enos-debug-data}" + fi + mkdir -p "${debug_dir}" + { + echo "debug-data-artifact-name=${artifact_name}" + echo "debug-data-dir=${debug_dir}" + } | tee -a "$GITHUB_OUTPUT" + + - id: launch + name: Run Enos scenario + continue-on-error: true + shell: bash + run: | + enos scenario launch \ + --timeout ${{ inputs.timeout }} \ + ${{ inputs.launch-extra-args }} \ + --chdir ${{ inputs.working-directory }} \ + ${{ inputs.scenario-filter }} + + - if: steps.launch.outcome == 'failure' && inputs.show-state-on-retry == 'true' + name: Show scenario state before retry + shell: bash + run: | + echo "::group::Scenario state before retry" + enos scenario exec --cmd show \ + --chdir ${{ inputs.working-directory }} \ + ${{ inputs.scenario-filter }} || true + echo "::endgroup::" + echo "::group::Scenario plan before retry" + enos scenario exec --cmd plan \ + --chdir ${{ inputs.working-directory }} \ + ${{ inputs.scenario-filter }} || true + echo "::endgroup::" + + - if: steps.launch.outcome == 'failure' + id: launch-retry + name: Retry Enos scenario + shell: bash + run: | + if ! output=$(enos scenario launch \ + --timeout ${{ inputs.retry-timeout }} \ + ${{ inputs.retry-extra-args }} \ + --chdir ${{ inputs.working-directory }} \ + ${{ inputs.scenario-filter }} 2>&1); then + if [ "${{ inputs.show-state-on-retry }}" = "true" ]; then + echo "::group::Scenario state after retry" + enos scenario exec --cmd show \ + --chdir ${{ inputs.working-directory }} \ + ${{ inputs.scenario-filter }} || true + echo "::endgroup::" + echo "::group::Scenario plan after retry" + enos scenario exec --cmd plan \ + --chdir ${{ inputs.working-directory }} \ + ${{ inputs.scenario-filter }} || true + echo "::endgroup::" + fi + echo "::group::Scenario retry failure" + echo "$output" >&2 + { + echo "# Enos Scenario Failed!" + echo 'See the [workflow](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more information' + echo '```' + echo "$output" + echo '```' + } | tee -a "$GITHUB_STEP_SUMMARY" + echo "::endgroup::" + exit 1 + fi + + - id: determine-launch-outcome + name: Determine launch outcome + shell: bash + run: | + retry_attempted=false + if [ "${{ steps.launch.outcome }}" = "failure" ]; then + retry_attempted=true + fi + if [ "${{ steps.launch.outcome }}" = "success" ] || [ "${{ steps.launch-retry.outcome }}" = "success" ]; then + outcome=success + else + outcome=failure + fi + { + echo "outcome=${outcome}" + echo "retry-attempted=${retry_attempted}" + } | tee -a "$GITHUB_OUTPUT" + + - if: failure() && inputs.upload-debug-data == 'true' + name: Upload debug data + continue-on-error: true + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: ${{ steps.prepare-artifacts.outputs.debug-data-artifact-name }} + path: ${{ steps.prepare-artifacts.outputs.debug-data-dir }} + retention-days: ${{ inputs.debug-data-retention-days }} + + - if: always() && inputs.pre-destroy-command != '' + id: pre-destroy + name: Run pre-destroy command + continue-on-error: true + shell: bash + run: | + enos scenario ${{ inputs.pre-destroy-command }} \ + --chdir ${{ inputs.working-directory }} \ + ${{ inputs.scenario-filter }} \ + ${{ inputs.pre-destroy-extra-args }} + + - if: always() + id: destroy + name: Destroy Enos scenario + continue-on-error: true + shell: bash + run: | + enos scenario destroy \ + --timeout ${{ inputs.destroy-timeout }} \ + ${{ inputs.destroy-extra-args }} \ + --chdir ${{ inputs.working-directory }} \ + ${{ inputs.scenario-filter }} + + - if: steps.destroy.outcome == 'failure' + id: destroy-retry + name: Retry Enos scenario destroy + continue-on-error: true + shell: bash + run: | + enos scenario destroy \ + --timeout ${{ inputs.destroy-timeout }} \ + ${{ inputs.destroy-extra-args }} \ + --chdir ${{ inputs.working-directory }} \ + ${{ inputs.scenario-filter }} + + - if: always() + id: determine-destroy-outcome + name: Determine destroy outcome + shell: bash + run: | + retry_attempted=false + if [ "${{ steps.destroy.outcome }}" = "failure" ]; then + retry_attempted=true + fi + if [ "${{ steps.destroy.outcome }}" = "success" ] || [ "${{ steps.destroy-retry.outcome }}" = "success" ]; then + outcome=success + else + outcome=failure + fi + { + echo "outcome=${outcome}" + echo "retry-attempted=${retry_attempted}" + } | tee -a "$GITHUB_OUTPUT" diff --git a/.github/actions/set-up-go/action.yml b/.github/actions/set-up-go/action.yml index f30bc3ea7c..89d7e95cbb 100644 --- a/.github/actions/set-up-go/action.yml +++ b/.github/actions/set-up-go/action.yml @@ -40,7 +40,7 @@ runs: else echo "go-version=${{ inputs.go-version }}" | tee -a "$GITHUB_OUTPUT" fi - - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version: ${{ steps.go-version.outputs.go-version }} cache: false # We use our own caching strategy @@ -63,7 +63,7 @@ runs: echo "cache-key=go-modules-${wd_hash}-${{ hashFiles('**/go.sum') }}" } | tee -a "$GITHUB_OUTPUT" - id: cache-modules - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: enableCrossOsArchive: true lookup-only: ${{ inputs.no-restore }} diff --git a/.github/actions/set-up-pipeline/action.yml b/.github/actions/set-up-pipeline/action.yml index 2206fc7d75..b73804e7b7 100644 --- a/.github/actions/set-up-pipeline/action.yml +++ b/.github/actions/set-up-pipeline/action.yml @@ -33,7 +33,7 @@ runs: } | tee -a "$GITHUB_ENV" - name: Try to restore pipeline from cache id: pipeline-cache - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ${{ env.PIPELINE_PATH }} key: pipeline-${{ env.PIPELINE_HASH }} diff --git a/.github/actions/setup-pnpm/action.yml b/.github/actions/setup-pnpm/action.yml new file mode 100644 index 0000000000..1027261f13 --- /dev/null +++ b/.github/actions/setup-pnpm/action.yml @@ -0,0 +1,32 @@ +# Copyright IBM Corp. 2016, 2025 +# SPDX-License-Identifier: BUSL-1.1 + + +# This action will set up Node and then install pnpm dependencies, which might be restored from cache if available. +# In case of a cache miss, pnpm dependencies will be installed and later be stored in the "post step" of actions/cache. +--- +name: Setup pnpm +description: Setup Node and install pnpm +runs: + using: composite + steps: + + - name: Install PNPM + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 + with: + run_install: false + package_json_file: './ui/package.json' + + - name: Setup Node Caching + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: './ui/package.json' + cache: pnpm + cache-dependency-path: ui/pnpm-lock.yaml + + - name: Install UI dependencies + working-directory: ./ui + shell: bash + run: | + pnpm i + pnpm rebuild node-sass diff --git a/.github/instructions/generic/ember_hbs.instructions.md b/.github/instructions/generic/ember_hbs.instructions.md index da67c4a51f..dcbff91e28 100644 --- a/.github/instructions/generic/ember_hbs.instructions.md +++ b/.github/instructions/generic/ember_hbs.instructions.md @@ -11,7 +11,7 @@ This document provides Handlebars template coding standards for HashiCorp Ember. ## Template Best Practices - Check truthiness of arrays directly instead of using `.length` property -- Use string interpolation `"prefix/{{value}}"` instead of `{{concat}}` helper +- Use string interpolation `"prefix/{{value}}"` instead of `{{concat}}` helper - Remove unnecessary quotes around dynamic component arguments - Use `Hds::Link::Inline` for external documentation links instead of `