Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ updates:
directory: "/bricks/test_optimizer/hooks"
schedule:
interval: "daily"
- package-ecosystem: "pub"
directory: "/bricks/spdx_license/hooks"
schedule:
interval: "daily"
- package-ecosystem: "pub"
directory: "/"
schedule:
Expand Down
53 changes: 53 additions & 0 deletions .github/workflows/spdx_license.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: spdx_license

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

on:
push:
paths:
- .github/workflows/spdx_license.yaml
- "bricks/spdx_license/**"
branches:
- main
pull_request:
paths:
- .github/workflows/spdx_license.yaml
- "bricks/spdx_license/**"
branches:
- main

jobs:
build_hooks:
defaults:
run:
working-directory: bricks/spdx_license/hooks
runs-on: ubuntu-latest
steps:
- name: 📚 Git Checkout
uses: actions/checkout@v4

- name: 🎯 Setup Dart
uses: dart-lang/setup-dart@v1
with:
sdk: 3.1.0

- name: 📦 Install Dependencies
run: dart pub get

- name: ✨ Check Formatting
run: dart format --set-exit-if-changed .

- name: 🕵️ Analyze
run: dart analyze --fatal-infos --fatal-warnings

- name: 🧪 Run Tests
run: |
dart pub global activate coverage 1.2.0
dart pub run test -j 4 --run-skipped --coverage=coverage --test-randomize-ordering-seed random && dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on="pre_gen,post_gen"

- name: 📊 Check Code Coverage
uses: VeryGoodOpenSource/very_good_coverage@v2.1.0
with:
path: bricks/spdx_license/hooks/coverage/lcov.info
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ coverage/
.idea

# Misc files
.DS_Store
.DS_Store

# Files generated by Mason
mason-lock.json
.mason
52 changes: 52 additions & 0 deletions bricks/spdx_license/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## 🦄 Contributing to the Very Good CLI SPDX License brick

First of all, thank you for taking the time to contribute! 🎉👍 Before you do, please carefully read this guide.

## Developing for Very Good CLI's SPDX License brick

To develop for Very Good CLI's SPDX License brick, you will also need to become familiar with our processes and conventions detailed [here](../../CONTRIBUTING.md).

💡 **Note**: The SPDX License brick is not published at [Brick Hub](brickhub.dev). It is not intended to be used by the general public. Instead, it has been designed to work closely with Very Good CLI.

### Setting up your local development environment

1. Install a valid [Dart SDK](https://dart.dev/get-dart) in your local environment. Compatible Dart SDK versions with the SPDX license brick can be found [here](https://github.com/VeryGoodOpenSource/very_good_cli/blob/main/bricks/spdx_license/hooks/pubspec.yaml). If you have Flutter installed you likely have a valid Dart SDK version already installed.

2. Install [Mason](https://github.com/felangel/mason/tree/master/packages/mason_cli#installation) in your local environment:

```sh
# 🎯 Activate from https://pub.dev
dart pub global activate mason_cli
```

3. Get hooks' dependencies:

```sh
# 🪝 Get hooks' dependencies (from bricks/spdx_license/hooks)
dart pub get
```

4. Run all hook's tests:

```sh
# 🧪 Test all hook's (from bricks/spdx_license/hooks)
dart test
```

If some tests do not pass out of the box, please submit an [issue](https://github.com/VeryGoodOpenSource/very_good_cli/issues/new/choose).

4. Get all Mason bricks:

```sh
# 🗂 Gets all bricks in the nearest mason.yaml (from project root)
mason get
```

5. Generate a Dart SPDX License enumeration:

```sh
# ⚙️ Generate code using the spdx_license brick (from within project)
mason make spdx_license -o lib/src/models/ --on-conflict=overwrite
```

If the licenses prompt is left empty the brick will fetch the [SPDX list](https://github.com/spdx/license-list-data/tree/main/json/details). Otherwise, the user specified licenses will be used and no SPDX List will be fetched.
34 changes: 34 additions & 0 deletions bricks/spdx_license/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# SPDX License

A generator that provides a Dart SPDX License enumeration from a list of SPDX licenses.

This package should always match [PANA's](https://github.com/dart-lang/pana/blob/master/third_party/spdx/update_licenses.dart) license list; currently the list is deduced from the [SPDX GitHub repository](https://github.com/spdx/license-list-data/tree/main/json/details).

💡 **Note**: The SPDX License brick is not published at [Brick Hub](brickhub.dev). It is not intended to be used by the general public. Instead, it has been designed to work closely with Very Good CLI.

## Usage

1. Install a valid [Dart SDK](https://dart.dev/get-dart) in your local environment. Compatible Dart SDK versions with the SPDX license brick can be found [here](https://github.com/VeryGoodOpenSource/very_good_cli/blob/main/bricks/spdx_license/hooks/pubspec.yaml). If you have Flutter installed you likely have a valid Dart SDK version already installed.

2. Install [Mason](https://github.com/felangel/mason/tree/master/packages/mason_cli#installation) in your local environment:

```sh
# 🎯 Activate from https://pub.dev
dart pub global activate mason_cli
```

3. Get all Mason bricks:

```sh
# 🗂 Gets all bricks in the nearest mason.yaml (from project root)
mason get
```

4. Generate a Dart SPDX License enumeration:

```sh
# ⚙️ Generate code using the spdx_license brick (from within project)
mason make spdx_license -o lib/src/models/ --on-conflict=overwrite
```

If the licenses prompt is left empty the brick will fetch the [SPDX list](https://github.com/spdx/license-list-data/tree/main/json/details). Otherwise, the user specified licenses will be used and no SPDX List will be fetched.
36 changes: 36 additions & 0 deletions bricks/spdx_license/__brick__/spdx_license.gen.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions bricks/spdx_license/brick.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: spdx_license
description: Generates a Dart SPDX License enumeration.
version: 0.1.0+1
publish_to: none

environment:
mason: ">=0.1.0-dev.51 <0.1.0"

vars:
licenses:
type: list
description: List of all SPDX licenses
1 change: 1 addition & 0 deletions bricks/spdx_license/hooks/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:very_good_analysis/analysis_options.5.1.0.yaml
3 changes: 3 additions & 0 deletions bricks/spdx_license/hooks/dart_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
tags:
pull-request-only:
skip: "Should only be run during pull request"
144 changes: 144 additions & 0 deletions bricks/spdx_license/hooks/pre_gen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/// A generator that creates the SPDX License enumeration.
///
/// For more information, see the `README.md`.
library spdx_license_pre_gen;

import 'package:archive/archive.dart';
import 'package:http/http.dart' as http;
import 'package:mason/mason.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;

/// The SPDX license list URL.
///
/// This is the URL of the SPDX license list data on GitHub. It should always
/// be consistent with PANA's [SPDX license list URL](https://github.com/dart-lang/pana/blob/master/third_party/spdx/update_licenses.dart).
///
/// See also:
///
/// * [PANA](https://github.com/dart-lang/pana), the Dart package analyzer.
const _spdxLicenseListUrl =
'https://github.com/spdx/license-list-data/archive/refs/heads/master.zip';

/// The license list path used by the PANA tool.
///
/// The [_spdxLicenseListUrl] has different paths for the license list data; the
/// PANA tool uses only those under the [_spdxTargetPath].
const _spdxTargetPath = 'license-list-data-main/json/details';

/// {@template generate_spdx_license_exception}
/// An exception thrown by the Generate SPDX License tool.
/// {@endtemplate}
class GenerateSpdxLicenseException implements Exception {
/// {@macro generate_spdx_license_exception}
const GenerateSpdxLicenseException(String message)
: message = '[spdx_license] $message';

final String message;
}

/// {@macro pre_gen}
Future<void> run(HookContext context) async => preGen(context);

/// {@template pre_gen}
/// Populates the context `licenses` variable with the SPDX license list, and
/// the `total` variable with the total number of licenses.
///
/// If the user decides to use their own license list, the `licenses` variable
/// will be populated with the user's list. Otherwise, the SPDX license list
/// will be downloaded and parsed from the same source as the PANA tool.
/// {@endtemplate}
@visibleForTesting
Future<void> preGen(
HookContext context, {
@visibleForTesting http.Client? client,
@visibleForTesting ZipDecoder? zipDecoder,
}) async {
try {
final licensesVar = context.vars['licenses'];
final shouldFetchLicenses =
(licensesVar == null || (licensesVar is List && licensesVar.isEmpty)) &&
licensesVar is! List<String>;

final licenses = shouldFetchLicenses
? await _downloadLicenses(
logger: context.logger,
client: client,
zipDecoder: zipDecoder,
)
: licensesVar as List;

final newLicensesVar = <Map<String, dynamic>>[
for (final license in licenses)
{
'license': license,
'identifier': license.toString().toDartIdentifier(),
},
];

context.vars = {
'licenses': newLicensesVar,
'total': newLicensesVar.length,
};
} on GenerateSpdxLicenseException catch (e) {
context.logger.err(e.message);
} catch (e) {
context.logger.err(
'''[spdx_license] An unknown error occurred, received error: $e''',
);
}
}

Future<List<String>> _downloadLicenses({
required Logger logger,
@visibleForTesting http.Client? client,
@visibleForTesting ZipDecoder? zipDecoder,
}) async {
final progress = logger.progress(
'Starting to download the SPDX license list, this might take some time...',
);

final httpClient = client ?? http.Client();
final response = await httpClient.get(Uri.parse(_spdxLicenseListUrl));

if (response.statusCode != 200) {
progress.cancel();
throw GenerateSpdxLicenseException(
'''Failed to download the SPDX license list, received response with status code: ${response.statusCode}''',
);
}

late final Archive archive;
try {
final decoder = zipDecoder ?? ZipDecoder();
archive = decoder.decodeBytes(response.bodyBytes);
} catch (e) {
progress.cancel();
throw GenerateSpdxLicenseException(
'Failed to decode the SPDX license list, received error: $e',
);
}

final licenses = <String>{};
for (final file in archive.files) {
final filename = file.name;
if (!filename.startsWith(_spdxTargetPath)) continue;

final license = path.basename(path.withoutExtension(filename));
licenses.add(license);
}

progress.complete('Found ${licenses.length} SPDX licenses');
return licenses.toList()..sort();
}

extension on String {
String toDartIdentifier() {
return '\$$this'
.replaceAll('-', '_')
.replaceAll('.', '_')
.replaceAll(' ', '')
.replaceAll('+', 'plus')
.trim();
}
}
21 changes: 21 additions & 0 deletions bricks/spdx_license/hooks/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: spdx_license_hooks
description: Hook for generating SPDX license.
version: 0.1.0+1
repository: https://github.com/VeryGoodOpenSource/very_good_cli
issue_tracker: https://github.com/VeryGoodOpenSource/very_good_cli/issues
publish_to: none

environment:
sdk: ">=3.0.0 <4.0.0"

dependencies:
archive: ^3.4.2
http: ^1.1.0
mason: ^0.1.0-dev.51
meta: ^1.10.0
path: ^1.8.3

dev_dependencies:
mocktail: ^1.0.0
test: ^1.19.2
very_good_analysis: ^5.1.0
Loading