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

Test against real-world Sass frameworks #1487

Merged
merged 2 commits into from Sep 17, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/workflows/ci.yml
Expand Up @@ -142,6 +142,68 @@ jobs:
--errors ambiguous-doc-reference,broken-link,deprecated
--errors unknown-directive,unknown-macro,unresolved-doc-reference

bootstrap:
name: "Bootstrap ${{ matrix.bootstrap_version }}"
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
bootstrap_version: [4, 5]

steps:
- uses: actions/checkout@v2
- uses: dart-lang/setup-dart@v1
- run: dart pub get
- run: dart pub run grinder fetch-bootstrap${{matrix.bootstrap_version}}
env: {GITHUB_BEARER_TOKEN: "${{ secrets.GITHUB_TOKEN }}"}
- name: Build
run: dart bin/sass.dart --quiet build/bootstrap/scss:build/bootstrap-output

bourbon:
name: Bourbon
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: dart-lang/setup-dart@v1
- run: dart pub get
- run: dart pub run grinder fetch-bourbon
env: {GITHUB_BEARER_TOKEN: "${{ secrets.GITHUB_TOKEN }}"}
- name: Test
run: |
dart bin/sass.dart --quiet -I build/bourbon -I build/bourbon/spec/fixtures \
build/bourbon/spec/fixtures:build/bourbon-output

foundation:
name: Foundation
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: dart-lang/setup-dart@v1
- run: dart pub get
- run: dart pub run grinder fetch-foundation
env: {GITHUB_BEARER_TOKEN: "${{ secrets.GITHUB_TOKEN }}"}
# TODO(nweiz): Foundation has proper Sass tests, but they're currently not
# compatible with Dart Sass. Once they are, we should run those rather
# than just building the CSS output.
- name: Build
run: dart bin/sass.dart --quiet build/foundation-sites/assets:build/foundation-output

bulma:
name: Bulma
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: dart-lang/setup-dart@v1
- run: dart pub get
- run: dart pub run grinder fetch-bulma
env: {GITHUB_BEARER_TOKEN: "${{ secrets.GITHUB_TOKEN }}"}
- name: Build
run: dart bin/sass.dart --quiet build/bulma/bulma.sass build/bulma-output.css

sanity_checks:
name: Sanity checks
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Expand Up @@ -34,7 +34,7 @@ dependencies:
dev_dependencies:
analyzer: ^2.2.0
archive: ^3.1.2
cli_pkg: ^1.3.0
cli_pkg: ^1.7.0
crypto: ^3.0.0
dart_style: ^2.0.0
grinder: ^0.9.0
Expand Down
5 changes: 3 additions & 2 deletions tool/grind.dart
Expand Up @@ -15,6 +15,7 @@ import 'grind/synchronize.dart';

export 'grind/bazel.dart';
export 'grind/benchmark.dart';
export 'grind/frameworks.dart';
export 'grind/sanity_check.dart';
export 'grind/subpackages.dart';
export 'grind/synchronize.dart';
Expand All @@ -34,8 +35,8 @@ void main(List<String> args) {
as Map<String, dynamic>;
pkg.npmReadme.fn = () => _readAndResolveMarkdown("package/README.npm.md");
pkg.standaloneName.value = "dart-sass";
pkg.githubUser.fn = () => Platform.environment["GH_USER"]!;
pkg.githubPassword.fn = () => Platform.environment["GH_TOKEN"]!;
pkg.githubUser.fn = () => Platform.environment["GH_USER"];
pkg.githubPassword.fn = () => Platform.environment["GH_TOKEN"];

pkg.githubReleaseNotes.fn = () =>
"To install Sass ${pkg.version}, download one of the packages below "
Expand Down
71 changes: 71 additions & 0 deletions tool/grind/frameworks.dart
@@ -0,0 +1,71 @@
// Copyright 2021 Google Inc. Use of this source code is governed by an
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'dart:convert';

import 'package:grinder/grinder.dart';
import 'package:http/http.dart' as http;

import 'utils.dart';

@Task('Download Bootstrap 5.x for testing purposes.')
Future<void> fetchBootstrap5() => _getLatestRelease('twbs/bootstrap');

@Task('Download Bootstrap 4.x for testing purposes.')
Future<void> fetchBootstrap4() =>
_getLatestRelease('twbs/bootstrap', pattern: RegExp(r'^v4\.'));

@Task('Download Bourbon for testing purposes.')
Future<void> fetchBourbon() => _getLatestRelease('thoughtbot/bourbon');

@Task('Download Foundation for testing purposes.')
Future<void> fetchFoundation() =>
_getLatestRelease('foundation/foundation-sites');

@Task('Download Bulma for testing purposes.')
Future<void> fetchBulma() => _getLatestRelease('jgthms/bulma');

/// Clones the latest release of the given GitHub repository [slug].
///
/// If [pattern] is passed, this will clone the latest release that matches that
/// pattern.
Future<void> _getLatestRelease(String slug, {Pattern? pattern}) async {
await cloneOrCheckout('git://github.com/$slug',
await _findLatestRelease(slug, pattern: pattern));
}

/// Returns the tag name of the latest release for the given GitHub repository
/// [slug].
///
/// If [pattern] is passed, this will find the latest release that matches that
/// pattern.
Future<String> _findLatestRelease(String slug, {Pattern? pattern}) async {
var releases = await _fetchReleases(slug);
if (pattern == null) return releases[0]['tag_name'] as String;

var page = 1;
while (releases.isNotEmpty) {
for (var release in releases) {
var tagName = release['tag_name'] as String;
if (pattern.allMatches(tagName).isNotEmpty) return tagName;
}

page++;
releases = await _fetchReleases(slug, page: page);
}

fail("Couldn't find a release of $slug matching $pattern.");
}

/// Fetches the GitHub releases page for the repo at [slug].
Future<List<Map<String, dynamic>>> _fetchReleases(String slug,
{int page = 1}) async {
var result = json.decode(await http.read(
Uri.parse("https://api.github.com/repos/$slug/releases?page=$page"),
headers: {
"accept": "application/vnd.github.v3+json",
"authorization": githubAuthorization
})) as List<dynamic>;
return result.cast<Map<String, dynamic>>();
}
15 changes: 3 additions & 12 deletions tool/grind/subpackages.dart
Expand Up @@ -12,6 +12,8 @@ import 'package:path/path.dart' as p;
import 'package:pubspec_parse/pubspec_parse.dart';
import 'package:yaml/yaml.dart';

import 'utils.dart';

/// The path in which pub expects to find its credentials file.
final String _pubCredentialsPath = () {
// This follows the same logic as pub:
Expand All @@ -30,17 +32,6 @@ final String _pubCredentialsPath = () {
return p.join(cacheDir, 'credentials.json');
}();

/// Returns the HTTP basic authentication Authorization header from the
/// environment.
String get _githubAuthorization {
var bearerToken = pkg.githubBearerToken.value;
return bearerToken != null
? "Bearer $bearerToken"
: "Basic " +
base64.encode(utf8
.encode(pkg.githubUser.value + ':' + pkg.githubPassword.value));
}

@Task('Deploy sub-packages to pub.')
Future<void> deploySubPackages() async {
// Write pub credentials
Expand Down Expand Up @@ -86,7 +77,7 @@ Future<void> deploySubPackages() async {
headers: {
"accept": "application/vnd.github.v3+json",
"content-type": "application/json",
"authorization": _githubAuthorization
"authorization": githubAuthorization
},
body: jsonEncode({
"ref": "refs/tags/${pubspec.name}/${pubspec.version}",
Expand Down
30 changes: 23 additions & 7 deletions tool/grind/utils.dart
Expand Up @@ -3,6 +3,7 @@
// https://opensource.org/licenses/MIT.

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

import 'package:cli_pkg/cli_pkg.dart' as pkg;
Expand All @@ -17,6 +18,17 @@ final sassBotEnvironment = RunOptions(environment: {
"GIT_COMMITTER_EMAIL": pkg.botEmail.value
});

/// Returns the HTTP basic authentication Authorization header from the
/// environment.
String get githubAuthorization {
var bearerToken = pkg.githubBearerToken.value;
return bearerToken != null
? "Bearer $bearerToken"
: "Basic " +
base64.encode(utf8
.encode(pkg.githubUser.value + ':' + pkg.githubPassword.value));
}

/// Ensure that the `build/` directory exists.
void ensureBuild() {
Directory('build').createSync(recursive: true);
Expand Down Expand Up @@ -47,18 +59,22 @@ Future<String> cloneOrCheckout(String url, String ref) async {

var path = p.join("build", name);

if (Directory(p.join(path, '.git')).existsSync()) {
log("Updating $url");
await runAsync("git",
arguments: ["fetch", "origin"], workingDirectory: path);
} else {
if (!Directory(p.join(path, '.git')).existsSync()) {
delete(Directory(path));
await runAsync("git", arguments: ["clone", url, path]);
await runAsync("git", arguments: ["init", path]);
await runAsync("git",
arguments: ["config", "advice.detachedHead", "false"],
workingDirectory: path);
await runAsync("git",
arguments: ["remote", "add", "origin", url], workingDirectory: path);
} else {
log("Updating $url");
}
await runAsync("git", arguments: ["checkout", ref], workingDirectory: path);

await runAsync("git",
arguments: ["fetch", "origin", "--depth=1", ref], workingDirectory: path);
await runAsync("git",
arguments: ["checkout", "FETCH_HEAD"], workingDirectory: path);
log("");

return path;
Expand Down