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

Format GitHub Actions files with sz format command #746

Merged
merged 9 commits into from
Aug 18, 2023
Merged
5 changes: 1 addition & 4 deletions .github/workflows/actions_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,11 @@ jobs:
needs: changes
runs-on: ubuntu-22.04
if: ${{ needs.changes.outputs.changesFound == 'true' }}
defaults:
run:
working-directory: .github/workflows
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9

- name: Install Prettier
run: npm install --global [email protected]

- name: Format
run: npx prettier . --check
run: ./bin/format_action_files.sh
14 changes: 14 additions & 0 deletions bin/format_action_files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
# Copyright (c) 2023 Sharezone UG (haftungsbeschränkt)
# Licensed under the EUPL-1.2-or-later.
#
# You may obtain a copy of the Licence at:
# https://joinup.ec.europa.eu/software/page/eupl
#
# SPDX-License-Identifier: EUPL-1.2

script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
get_cmd="$script_dir/source_of_truth/get_sot_cmd.sh"

format_action_files=$($get_cmd format_action_files)
eval $"($format_action_files)"
1 change: 1 addition & 0 deletions bin/source_of_truth/commands_source_of_truth.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
# The programs using these commands need to add it themselves.
check_license_headers: addlicense -check -c "Sharezone UG (haftungsbeschränkt)" -f header_template.txt -ignore "**/GeneratedPluginRegistrant.swift" -ignore "**/**.g.dart" -ignore "**/**.mocks.dart"
add_license_headers: addlicense -c "Sharezone UG (haftungsbeschränkt)" -f header_template.txt -ignore "**/GeneratedPluginRegistrant.swift" -ignore "**/**.g.dart" -ignore "**/**.mocks.dart"
format_action_files: prettier ".github/workflows/" --write
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ import 'dart:async';
import 'dart:io';

import 'package:args/command_runner.dart';

import 'package:sz_repo_cli/src/commands/src/check_license_headers_command.dart';
import 'package:sz_repo_cli/src/common/common.dart';

import 'check_license_headers_command.dart';

/// Add license headers to all files without one.
class AddLicenseHeadersCommand extends Command {
AddLicenseHeadersCommand(this.repo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'dart:io';

import 'package:args/command_runner.dart';
import 'package:sz_repo_cli/src/common/common.dart';
import 'package:yaml/yaml.dart';
import 'package:sz_repo_cli/src/common/src/run_source_of_truth_command.dart';

/// Check that all files have correct license headers.
class CheckLicenseHeadersCommand extends Command {
Expand Down Expand Up @@ -51,75 +51,9 @@ Future<ProcessResult> runLicenseHeaderCommand({
required String commandKey,
required SharezoneRepo repo,
}) {
final sot = repo.commandsSourceOfTruthYamlFile;
final commands = loadYaml(sot.readAsStringSync()) as Map;
final command = commands[commandKey] as String;
final splitted = command.split(' ');
final executable = splitted[0];
var argumentsString = command.replaceFirst('$executable ', '');
final arguments = _convertIntoArgumentsList(argumentsString)
..add(repo.location.path);

return runProcess(executable, arguments,
workingDirectory: repo.location.path);
}

/// Converts command line arguments into a List<String>.
///
/// The methods to run commands via [Process] expect a single "command string"
/// (e.g. `addlicense`) and a List<String> for arguments (example below).
///
/// If we read a command String from somewhere else we will need to split this
/// string into its arguments via this method.
///
/// Input: -check -c "Sharezone UG (haftungsbeschränkt)" -f header_template.txt -ignore "**/GeneratedPluginRegistrant.swift" -ignore "**/**.g.dart"
/// Output: [-check, -c, Sharezone UG (haftungsbeschränkt), -f, header_template.txt, -ignore, **/GeneratedPluginRegistrant.swift, -ignore, **/**.g.dart, f:\development\projects\sharezone-app]
List<String> _convertIntoArgumentsList(String input) {
final result = <String>[];

var current = '';

String? quoteToken;
var wasLastTokenQuoted = false;

for (var index = 0; index < input.length; index++) {
final token = input[index];

if (quoteToken != null) {
if (token == quoteToken) {
wasLastTokenQuoted = true;
quoteToken = null;
} else {
current += token;
}
} else {
switch (token) {
case "'":
case '"':
quoteToken = token;
continue;

case ' ':
if (wasLastTokenQuoted || current.isNotEmpty) {
result.add(current);
current = '';
}
break;

default:
current += token;
wasLastTokenQuoted = false;
}
}
}

if (wasLastTokenQuoted || current.isNotEmpty) {
result.add(current);
}

if (quoteToken != null) {
throw Exception('Unbalanced quote $quoteToken in input:\n$input');
}

return result;
return runSourceOfTruthCommand(
commandKey: commandKey,
repo: repo,
argumentsToAppend: [repo.location.path],
);
}
49 changes: 47 additions & 2 deletions tools/sz_repo_cli/lib/src/commands/src/format_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
// SPDX-License-Identifier: EUPL-1.2

import 'dart:async';
import 'dart:io';

import 'package:sz_repo_cli/src/common/common.dart';
import 'package:sz_repo_cli/src/common/src/run_source_of_truth_command.dart';

class FormatCommand extends ConcurrentCommand {
FormatCommand(SharezoneRepo repo) : super(repo);
Expand All @@ -17,14 +19,57 @@ class FormatCommand extends ConcurrentCommand {
final String name = 'format';

@override
final String description = 'Formats packages via "dart format".\n'
final String description = 'Formats our source code.\n'
'Does not fail if packages are not formatted properly.';

@override
Duration get defaultPackageTimeout => const Duration(minutes: 3);

@override
Future<void> runTaskForPackage(Package package) => formatCode(package);
Future<void> runSetup() async {
await _throwIfPrettierIsNotInstalled();

await _formatActionFiles(repo: repo);
}

@override
Future<void> runTaskForPackage(Package package) async =>
await formatCode(package);

Future<void> _throwIfPrettierIsNotInstalled() async {
if (Platform.isWindows) {
// We skip this check on Windows because "which -s" is not available.
return;
}

// Check if "which -s prettier" returns 0.
// If not, throw an exception.
final result = await runProcess(
'which',
['-s', 'prettier'],
);
if (result.exitCode != 0) {
throw Exception(
'Prettier is not installed. Run `npm install -g prettier` to install it.',
);
}
}

Future<void> _formatActionFiles({
required SharezoneRepo repo,
}) async {
final results = await runSourceOfTruthCommand(
commandKey: 'format_action_files',
repo: repo,
);

if (results.exitCode != 0) {
throw Exception(
'The process exited with a non-zero code (${results.exitCode})\n${results.stdout}\n${results.stderr}');
}

stdout.writeln('✅ Formatted GitHub Action files.');
}
}

Future<void> formatCode(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) 2023 Sharezone UG (haftungsbeschränkt)
// Licensed under the EUPL-1.2-or-later.
//
// You may obtain a copy of the Licence at:
// https://joinup.ec.europa.eu/software/page/eupl
//
// SPDX-License-Identifier: EUPL-1.2

import 'dart:io';

import 'package:sz_repo_cli/src/common/src/run_process.dart';
import 'package:sz_repo_cli/src/common/src/sharezone_repo.dart';
import 'package:yaml/yaml.dart';

/// Run a source of truth command via a key.
///
/// The key can be seen in the [repo.commandsSourceOfTruthYamlFile] file.
Future<ProcessResult> runSourceOfTruthCommand({
required String commandKey,
required SharezoneRepo repo,

/// Arguments that will be append to the arguments of the command.
List<String> argumentsToAppend = const [],
}) {
final sot = repo.commandsSourceOfTruthYamlFile;
final commands = loadYaml(sot.readAsStringSync()) as Map;
final command = commands[commandKey] as String;
final splitted = command.split(' ');
final executable = splitted[0];
var argumentsString = command.replaceFirst('$executable ', '');
final arguments = _convertIntoArgumentsList(argumentsString)
..addAll(argumentsToAppend);

return runProcess(
executable,
arguments,
workingDirectory: repo.location.path,
);
}

/// Converts command line arguments into a List<String>.
///
/// The methods to run commands via [Process] expect a single "command string"
/// (e.g. `addlicense`) and a List<String> for arguments (example below).
///
/// If we read a command String from somewhere else we will need to split this
/// string into its arguments via this method.
///
/// Input: -check -c "Sharezone UG (haftungsbeschränkt)" -f header_template.txt -ignore "**/GeneratedPluginRegistrant.swift" -ignore "**/**.g.dart"
/// Output: [-check, -c, Sharezone UG (haftungsbeschränkt), -f, header_template.txt, -ignore, **/GeneratedPluginRegistrant.swift, -ignore, **/**.g.dart]
List<String> _convertIntoArgumentsList(String input) {
final result = <String>[];

var current = '';

String? quoteToken;
var wasLastTokenQuoted = false;

for (var index = 0; index < input.length; index++) {
final token = input[index];

if (quoteToken != null) {
if (token == quoteToken) {
wasLastTokenQuoted = true;
quoteToken = null;
} else {
current += token;
}
} else {
switch (token) {
case "'":
case '"':
quoteToken = token;
continue;

case ' ':
if (wasLastTokenQuoted || current.isNotEmpty) {
result.add(current);
current = '';
}
break;

default:
current += token;
wasLastTokenQuoted = false;
}
}
}

if (wasLastTokenQuoted || current.isNotEmpty) {
result.add(current);
}

if (quoteToken != null) {
throw Exception('Unbalanced quote $quoteToken in input:\n$input');
}

return result;
}