diff --git a/.github/workflows/_gha_build.yaml b/.github/workflows/_gha_build.yaml new file mode 100644 index 0000000..6abc9de --- /dev/null +++ b/.github/workflows/_gha_build.yaml @@ -0,0 +1,33 @@ +name: 🏗️ CI + +on: + pull_request: + push: + branches: + - master + tags: + - '**' + +permissions: + contents: read + id-token: write + +jobs: + build: + runs-on: [self-hosted, xs, al2023] + steps: + - uses: Workiva/gha-setup-credentials@v2.0.2 + # Even though we aren't uploading a specific artifact, the + # store artifacts action uploads a build.txt and marks the + # github action run as "the build" for integration with + # workiva release pipelines + - uses: Workiva/gha-store-artifacts@v2.0.1 + test: + runs-on: [self-hosted, xs, al2023] + steps: + - uses: Workiva/gha-setup-credentials@v2.0.2 + # Even though we have no test artifacts to actually upload + # we need to run gha-upload-test-reports to satisfy the + # auto QA pipeline rule + - uses: Workiva/gha-upload-test-reports@v2.0.2 + diff --git a/.github/workflows/_test:checks.yaml b/.github/workflows/_test:checks.yaml new file mode 100644 index 0000000..c4b268b --- /dev/null +++ b/.github/workflows/_test:checks.yaml @@ -0,0 +1,25 @@ +name: 🧪 checks + +on: + push: + branches: + - master + pull_request: + paths: + - __tests__/fixtures/checks/** + - .github/workflows/checks.yaml + - .github/workflows/_test:checks.yaml + +jobs: + checks: + uses: ./.github/workflows/checks.yaml + with: + package-path: __tests__/fixtures/checks + + checks-with-additional: + uses: ./.github/workflows/checks.yaml + with: + package-path: __tests__/fixtures/checks + additional-checks: | + echo "all good!" + dart format diff --git a/.github/workflows/_test:test-unit.yaml b/.github/workflows/_test:test-unit.yaml new file mode 100644 index 0000000..c353209 --- /dev/null +++ b/.github/workflows/_test:test-unit.yaml @@ -0,0 +1,22 @@ +name: 🧪 test-unit + +on: + push: + branches: + - master + pull_request: + paths: + - __tests__/fixtures/test-unit/** + - .github/workflows/test-unit.yaml + - .github/workflows/_test:test-unit.yaml + +jobs: + test-unit-without-chrome: + uses: ./.github/workflows/test-unit.yaml + with: + package-path: __tests__/fixtures/test-unit/without-chrome + + test-unit-with-chrome: + uses: ./.github/workflows/test-unit.yaml + with: + package-path: __tests__/fixtures/test-unit/with-chrome \ No newline at end of file diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml new file mode 100644 index 0000000..0af4559 --- /dev/null +++ b/.github/workflows/checks.yaml @@ -0,0 +1,107 @@ +on: + workflow_call: + inputs: + package-path: + description: The path to the package to run checks on + default: '.' + type: string + + additional-checks: + description: > + Any commands to run along with the hardcoded defaults. Enter multiple + commands on different lines. Failure conditions are based on if any + provided command results in a non-zero exit code, OR modifies the source. + type: string + + sdk: + description: See https://github.com/dart-lang/setup-dart + default: 2.19.6 + type: string + +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + with: + sdk: ${{ inputs.sdk }} + - working-directory: ${{ inputs.package-path }} + run: dart run dart_dev analyze + + + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + with: + sdk: ${{ inputs.sdk }} + - name: Run format + working-directory: ${{ inputs.package-path }} + run: dart run dart_dev format --check + + dependency-validator: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + with: + sdk: ${{ inputs.sdk }} + + - name: Install Dependency Validator + run: dart pub global activate dependency_validator ^3.2.3 + + - name: Install Package Dependencies + working-directory: ${{ inputs.package-path }} + run: dart pub get + + - name: Run Dependency Validator + working-directory: ${{ inputs.package-path }} + run: dart pub global run dependency_validator + + analyze-additional-checks: + runs-on: ubuntu-latest + if: inputs.additional-checks != '' + outputs: + checks: ${{ steps.analyze.outputs.checks }} + steps: + - name: Analyze Checks + id: analyze + run: | + checks=$(cat <<-END + ${{ inputs.additional-checks }} + END) + checks_json=$(echo "$checks" | jq -R . | jq -scM .) + echo "checks=$checks_json" >> $GITHUB_OUTPUT + + additional-checks: + runs-on: ubuntu-latest + needs: analyze-additional-checks + strategy: + fail-fast: false + matrix: + check: ${{ fromJson(needs.analyze-additional-checks.outputs.checks) }} + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + with: + sdk: ${{ inputs.sdk }} + + - name: Install Package Dependencies + working-directory: ${{ inputs.package-path }} + run: dart pub get + + - name: Run '${{ matrix.check }}' + run: ${{ matrix.check }} + working-directory: ${{ inputs.package-path }} + + - name: Check for source changes + run: | + git diff --quiet -- . ':(exclude)**pubspec.lock' || { + echo "::error::The '${{ matrix.check }}' failed with source changes" + git --no-pager diff -- . ':(exclude)**pubspec.lock' + exit 1 + } + + diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..298ccf6 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,37 @@ +on: + workflow_call: + inputs: + sdk: + description: See https://github.com/dart-lang/setup-dart + default: 2.19.6 + type: string + +jobs: + create-sbom-release-asset: + name: Create SBOM Release Asset + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Publish SBOM to Release Assets + uses: anchore/sbom-action@v0 + with: + path: ./ + format: cyclonedx-json + + publish: + name: Publish to pub.dev + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + with: + sdk: ${{ inputs.sdk }} + + - name: Install dependencies + run: dart pub get + + - name: Publish - dry run + run: dart pub publish --dry-run + + - name: Publish + run: dart pub publish -f \ No newline at end of file diff --git a/.github/workflows/test-unit.yaml b/.github/workflows/test-unit.yaml new file mode 100644 index 0000000..6be062a --- /dev/null +++ b/.github/workflows/test-unit.yaml @@ -0,0 +1,113 @@ +on: + workflow_call: + inputs: + preset: + description: > + Any preset to specify when running dart test, see the 'test' packages + documentation on presets for more information. + type: string + + browser-aggregation: + description: > + Whether or not to run the test with the --browser-aggregation flag. See test_html_builder's + readme for more information: https://github.com/Workiva/test_html_builder#aggregating-browser-tests + default: false + type: boolean + + test-inputs: + description: Optional path(s) to a specific test file(s) passed to the dart test command + type: string + + test-args: + description: Arguments passed directly to the dart test command + type: string + + package-path: + description: A path to the package to run unit tests against + type: string + default: '.' + + timeout: + description: A timeout, specificed in minutes, for the dart test command + default: 15 + type: number + + sdk: + description: See https://github.com/Workiva/gha-dart/blob/master/setup-dart/README.md + default: 2.19.6 + type: string + +jobs: + pre: + runs-on: ubuntu-latest + outputs: + has-chrome-platform: ${{ steps.analyze.outputs.HAS_CHROME_PLATFORM }} + steps: + - uses: actions/checkout@v4 + - name: Install yq + run: | + sudo wget -O /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.43.1/yq_linux_amd64 + sudo chmod +x /usr/local/bin/yq + - working-directory: ${{ inputs.package-path }} + id: analyze + run: | + if [[ -f 'dart_test.yaml' ]]; then + platforms=$(yq '.platforms[], .presets.*.platforms[]' dart_test.yaml) + + if [[ "$platforms" == *"chrome"* ]]; then + echo "Project has chrome platform config" + echo "HAS_CHROME_PLATFORM=true" >> $GITHUB_OUTPUT + exit 0 + fi + fi + echo "Project does not have chrome platform config" + echo "HAS_CHROME_PLATFORM=false" >> $GITHUB_OUTPUT + + unit: + runs-on: ubuntu-latest + needs: pre + name: ${{ matrix.release-mode && 'dev' || 'release' }} + strategy: + fail-fast: false + matrix: + release-mode: ${{ fromJson(needs.pre.outputs.has-chrome-platform == 'true' && '[true, false]' || '[false]') }} + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + with: + sdk: ${{ inputs.sdk }} + + - name: Setup Chrome + if: needs.pre.outputs.has-chrome-platform == 'true' + uses: browser-actions/setup-chrome@v1 + with: + chrome-version: stable + - name: Setup Chrome Alias + if: needs.pre.outputs.has-chrome-platform == 'true' + run: alias google-chrome="/opt/hostedtoolcache/chromium/stable/x64/chrome" + + - name: Pub get + working-directory: ${{ inputs.package-path }} + run: dart pub get + + - name: Run dart_dev unit tests + working-directory: ${{ inputs.package-path }} + run: | + args=() + if [[ "${{ matrix.release-mode }}" == "true" ]]; then + args+=(--release) + fi + + if [[ "${{ inputs.browser-aggregation }}" == "true" ]]; then + args+=(--browser-aggregation) + fi + + if [[ "${{ inputs.preset }}" != "" ]]; then + args+=(-P ${{ inputs.preset }}) + fi + + if [[ "${{ inputs.test-args }}" != "" ]]; then + args+=(--test-args="${{ inputs.test-args }}") + fi + + timeout ${{ inputs.timeout }}m dart run dart_dev test "${args[@]}" ${{ inputs.test-inputs }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8def5f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +pubspec.lock +.dart_tool \ No newline at end of file diff --git a/README.md b/README.md index 22ab994..968395d 100644 --- a/README.md +++ b/README.md @@ -1 +1,47 @@ -# gha-dart-oss \ No newline at end of file +# gha-dart-oss + +Workflows for use with Dart projects. + +The majority of workflows assume [dart_dev](https://github.com/Workiva/dart_dev) is installed and being used within the repo + + +## Basic Dart CI Setup +```yaml +name: CI + +on: + pull_request: + push: + branches: + - master + tags: + - '**' + +jobs: + # Runs analysis, formatting, and dependency validation, against the dart source + checks: + uses: Workiva/gha-dart-oss/.github/workflows/checks.yaml@v1.0.0 + + # Runs unit tests in both d2js and ddc + unit-tests: + uses: Workiva/gha-dart-oss/.github/workflows/test-unit.yaml@v1.0.0 +``` + +```yaml +name: Publish + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' + +permissions: + contents: write + id-token: write + pull-requests: write + +jobs: + # Generates and uploads sbom, and publishes to pub.dev + publish: + uses: Workiva/gha-dart-oss/.github/workflows/publish@v1.0.0 +``` \ No newline at end of file diff --git a/__tests__/fixtures/checks/lib/main.dart b/__tests__/fixtures/checks/lib/main.dart new file mode 100644 index 0000000..d254ccd --- /dev/null +++ b/__tests__/fixtures/checks/lib/main.dart @@ -0,0 +1,9 @@ +final newVar = 'asdf'; + +String foo(int inp) { + return inp.toString(); +} + +void main() { + print(foo(4)); +} diff --git a/__tests__/fixtures/checks/pubspec.yaml b/__tests__/fixtures/checks/pubspec.yaml new file mode 100644 index 0000000..1408ba0 --- /dev/null +++ b/__tests__/fixtures/checks/pubspec.yaml @@ -0,0 +1,7 @@ +name: checks_test +publish_to: none +environment: + sdk: '>=2.10.0 <3.0.0' + +dev_dependencies: + dart_dev: ^4.1.1 \ No newline at end of file diff --git a/__tests__/fixtures/test-unit/with-chrome/dart_test.yaml b/__tests__/fixtures/test-unit/with-chrome/dart_test.yaml new file mode 100644 index 0000000..1248b69 --- /dev/null +++ b/__tests__/fixtures/test-unit/with-chrome/dart_test.yaml @@ -0,0 +1,2 @@ +platforms: + - chrome \ No newline at end of file diff --git a/__tests__/fixtures/test-unit/with-chrome/pubspec.yaml b/__tests__/fixtures/test-unit/with-chrome/pubspec.yaml new file mode 100644 index 0000000..439b36e --- /dev/null +++ b/__tests__/fixtures/test-unit/with-chrome/pubspec.yaml @@ -0,0 +1,10 @@ +name: unit_test_with_chrome +publish_to: none +environment: + sdk: '>=2.11.0 <3.0.0' + +dependencies: + build_runner: ^2.0.0 + build_web_compilers: ^3.0.0 + test: ^1.21.1 + dart_dev: ^4.0.2 \ No newline at end of file diff --git a/__tests__/fixtures/test-unit/with-chrome/test/main_test.dart b/__tests__/fixtures/test-unit/with-chrome/test/main_test.dart new file mode 100644 index 0000000..1a8235a --- /dev/null +++ b/__tests__/fixtures/test-unit/with-chrome/test/main_test.dart @@ -0,0 +1,10 @@ +import 'dart:html'; + +import 'package:test/test.dart'; + +void main() { + test('some test', () { + document.body.append(DivElement()..id = 'someId'); + expect(document.body.querySelector('div').id, 'someId'); + }); +} \ No newline at end of file diff --git a/__tests__/fixtures/test-unit/without-chrome/dart_test.yaml b/__tests__/fixtures/test-unit/without-chrome/dart_test.yaml new file mode 100644 index 0000000..414745a --- /dev/null +++ b/__tests__/fixtures/test-unit/without-chrome/dart_test.yaml @@ -0,0 +1,3 @@ +presets: + some_preset: + chain_stack_traces: true \ No newline at end of file diff --git a/__tests__/fixtures/test-unit/without-chrome/pubspec.yaml b/__tests__/fixtures/test-unit/without-chrome/pubspec.yaml new file mode 100644 index 0000000..a25a0ed --- /dev/null +++ b/__tests__/fixtures/test-unit/without-chrome/pubspec.yaml @@ -0,0 +1,8 @@ +name: unit_test +publish_to: none +environment: + sdk: '>=2.11.0 <3.0.0' + +dependencies: + test: ^1.21.1 + dart_dev: ^4.0.2 diff --git a/__tests__/fixtures/test-unit/without-chrome/test/main_test.dart b/__tests__/fixtures/test-unit/without-chrome/test/main_test.dart new file mode 100644 index 0000000..45fa263 --- /dev/null +++ b/__tests__/fixtures/test-unit/without-chrome/test/main_test.dart @@ -0,0 +1,7 @@ +import 'package:test/test.dart'; + +void main() { + test('some test', () { + expect(1, 1); + }); +} \ No newline at end of file diff --git a/aviary.yaml b/aviary.yaml new file mode 100644 index 0000000..c44b49c --- /dev/null +++ b/aviary.yaml @@ -0,0 +1,4 @@ +version: 1 + +exclude: + - ^__tests__/fixtures/ diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..60453e6 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +v1.0.0 \ No newline at end of file