Skip to content

Commit 5a38476

Browse files
committed
fix triage and other cleanup
1 parent 863a8b0 commit 5a38476

8 files changed

+154
-39
lines changed

Dockerfile

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1+
# Official Dart image: https://hub.docker.com/_/dart
12
# Specify the Dart SDK base image version using dart:<version> (ex: dart:2.12)
2-
FROM dart:2.12 AS build
3+
FROM dart:stable AS build
34

45
# Resolve app dependencies.
56
WORKDIR /app
6-
COPY pubspec.* .
7+
COPY pubspec.* ./
78
RUN dart pub get
89

910
# Copy app source code and AOT compile it.
1011
COPY . .
12+
# Ensure packages are still up-to-date if anything has changed
1113
RUN dart pub get --offline
12-
RUN dart compile exe bin/server.dart -o /server
14+
RUN dart compile exe bin/server.dart -o bin/server
1315

1416
# Build minimal serving image from AOT-compiled `/server` and required system
1517
# libraries and configuration files stored in `/runtime/` from the build stage.
1618
FROM scratch
1719
COPY --from=build /runtime/ /
18-
COPY --from=build /server /bin/
20+
COPY --from=build /app/bin/server /app/bin/
21+
22+
# Include files in the /static directory to enable static asset handling
23+
COPY --from=build /app/static/ /static
1924

2025
# Start server.
2126
EXPOSE 8080
22-
CMD ["/bin/server"]
27+
CMD ["/app/bin/server"]

analysis_options.yaml

-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ include: package:pedantic/analysis_options.yaml
22
analyzer:
33
strong-mode:
44
implicit-casts: false
5-
errors:
6-
todo: ignore
7-
unused_import: error
8-
unused_element: error
9-
unused_local_variable: error
10-
dead_code: error
115

126
linter:
137
rules:

bin/server.dart

+1-16
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,20 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'dart:async';
6-
import 'dart:io';
76

87
import 'package:dartbug/server.dart';
98
import 'package:dartbug/utils.dart';
109
import 'package:shelf/shelf.dart';
11-
import 'package:shelf/shelf_io.dart';
1210

1311
Future main() async {
14-
// Find port to listen on from environment variable.
15-
final port = int.parse(Platform.environment['PORT'] ?? '8080');
16-
1712
var pipeline = const Pipeline();
1813

19-
InternetAddress listenAddress;
20-
2114
if (kEntries.isNotEmpty) {
2215
print('Assuming environment is Cloud Run\n${kEntries.join('\n')}');
23-
listenAddress = InternetAddress.anyIPv4;
2416
} else {
2517
// Only add log middleware if we're not on Cloud Run
2618
pipeline = pipeline.addMiddleware(logRequests());
27-
listenAddress = InternetAddress.loopbackIPv4;
2819
}
2920

30-
// Serve handler on given port.
31-
final server = await serve(
32-
pipeline.addHandler(handler),
33-
listenAddress,
34-
port,
35-
);
36-
print('Serving at http://${server.address.host}:${server.port}');
21+
await serveHandler(handler);
3722
}

lib/redirect.dart

+1-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
import 'dart:convert';
66
import 'dart:io';
77

8-
import 'utils.dart';
9-
108
// Environment constants
119
const gitHub = 'https://github.com';
1210
const organization = 'dart-lang';
@@ -54,7 +52,7 @@ String? _checkMatch(RegExp re, String path) {
5452

5553
late final List<String> _areaLabels = List<String>.from(
5654
jsonDecode(
57-
File(fixPath('lib/sdk_labels.json')).readAsStringSync(),
55+
File('static/sdk_labels.json').readAsStringSync(),
5856
) as List,
5957
)..removeWhere((label) => !label.startsWith('area-'));
6058

lib/server.dart

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import 'dart:io';
99
import 'package:shelf/shelf.dart';
1010

1111
import 'redirect.dart';
12-
import 'utils.dart';
1312

1413
int _infoRequests = 0;
1514
int _redirects = 0;
@@ -54,7 +53,7 @@ Allow: /
5453
);
5554
case 'favicon.ico':
5655
return Response.ok(
57-
File(fixPath('static/favicon.ico')).readAsBytesSync(),
56+
File('static/favicon.ico').readAsBytesSync(),
5857
headers: {'Content-Type': 'image/x-icon'},
5958
);
6059
}

lib/utils.dart

+135-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,145 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:async';
56
import 'dart:io';
67

7-
String fixPath(String targetPath) => [
8-
if (kEntries.isNotEmpty) '/app/',
9-
targetPath,
10-
].join('/');
8+
import 'package:http/http.dart' as http;
9+
import 'package:shelf/shelf.dart';
10+
import 'package:shelf/shelf_io.dart';
1111

1212
final kEntries = Platform.environment.entries
1313
.where((e) => e.key.startsWith('K_'))
1414
.map((e) => '${e.key}\t${e.value}')
1515
.toList();
16+
17+
/// Serves [handler] on [InternetAddress.anyIPv4] using the port returned by
18+
/// [listenPort].
19+
///
20+
/// The returned [Future] will complete using [terminateRequestFuture] after
21+
/// closing the server.
22+
Future<void> serveHandler(Handler handler) async {
23+
final port = listenPort();
24+
25+
final server = await serve(
26+
handler,
27+
InternetAddress.anyIPv4, // Allows external connections
28+
port,
29+
);
30+
print('Serving at http://${server.address.host}:${server.port}');
31+
32+
await terminateRequestFuture();
33+
34+
await server.close();
35+
}
36+
37+
/// Returns the port to listen on from environment variable or uses the default
38+
/// `8080`.
39+
///
40+
/// See https://cloud.google.com/run/docs/reference/container-contract#port
41+
int listenPort() => int.parse(Platform.environment['PORT'] ?? '8080');
42+
43+
/// Returns a [Future] that completes when the process receives a
44+
/// [ProcessSignal] requesting a shutdown.
45+
///
46+
/// [ProcessSignal.sigint] is listened to on all platforms.
47+
///
48+
/// [ProcessSignal.sigterm] is listened to on all platforms except Windows.
49+
Future<void> terminateRequestFuture() {
50+
final completer = Completer<bool>.sync();
51+
52+
// sigIntSub is copied below to avoid a race condition - ignoring this lint
53+
// ignore: cancel_subscriptions
54+
StreamSubscription? sigIntSub, sigTermSub;
55+
56+
Future<void> signalHandler(ProcessSignal signal) async {
57+
print('Received signal $signal - closing');
58+
59+
final subCopy = sigIntSub;
60+
if (subCopy != null) {
61+
sigIntSub = null;
62+
await subCopy.cancel();
63+
sigIntSub = null;
64+
if (sigTermSub != null) {
65+
await sigTermSub!.cancel();
66+
sigTermSub = null;
67+
}
68+
completer.complete(true);
69+
}
70+
}
71+
72+
sigIntSub = ProcessSignal.sigint.watch().listen(signalHandler);
73+
74+
// SIGTERM is not supported on Windows. Attempting to register a SIGTERM
75+
// handler raises an exception.
76+
if (!Platform.isWindows) {
77+
sigTermSub = ProcessSignal.sigterm.watch().listen(signalHandler);
78+
}
79+
80+
return completer.future;
81+
}
82+
83+
/// Returns a [Future] that completes with the
84+
/// [Project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects)
85+
/// for the current Google Cloud Project.
86+
///
87+
/// First, if an environment variable with a name in
88+
/// [gcpProjectIdEnvironmentVariables] exists, that is returned.
89+
/// (The list is checked in order.) This is useful for local development.
90+
///
91+
/// If no such environment variable exists, then we assume the code is running
92+
/// on Google Cloud and
93+
/// [Project metadata](https://cloud.google.com/compute/docs/metadata/default-metadata-values#project_metadata)
94+
/// is queried for the Project ID.
95+
Future<String> currentProjectId() async {
96+
for (var envKey in gcpProjectIdEnvironmentVariables) {
97+
final value = Platform.environment[envKey];
98+
if (value != null) return value;
99+
}
100+
101+
const host = 'http://metadata.google.internal/';
102+
final url = Uri.parse('$host/computeMetadata/v1/project/project-id');
103+
104+
try {
105+
final response = await http.get(
106+
url,
107+
headers: {'Metadata-Flavor': 'Google'},
108+
);
109+
110+
if (response.statusCode != 200) {
111+
throw HttpException(
112+
'${response.body} (${response.statusCode})',
113+
uri: url,
114+
);
115+
}
116+
117+
return response.body;
118+
} on SocketException {
119+
stderr.writeln(
120+
'''
121+
Could not connect to $host.
122+
If not running on Google Cloud, one of these environment variables must be set
123+
to the target Google Project ID:
124+
${gcpProjectIdEnvironmentVariables.join('\n')}
125+
''',
126+
);
127+
rethrow;
128+
}
129+
}
130+
131+
/// A set of typical environment variables that are likely to represent the
132+
/// current Google Cloud project ID.
133+
///
134+
/// For context, see:
135+
/// * https://cloud.google.com/functions/docs/env-var
136+
/// * https://cloud.google.com/compute/docs/gcloud-compute#default_project
137+
/// * https://github.com/GoogleContainerTools/gcp-auth-webhook/blob/08136ca171fe5713cc70ef822c911fbd3a1707f5/server.go#L38-L44
138+
///
139+
/// Note: these are ordered starting from the most current/canonical to least.
140+
/// (At least as could be determined at the time of writing.)
141+
const gcpProjectIdEnvironmentVariables = {
142+
'GCP_PROJECT',
143+
'GCLOUD_PROJECT',
144+
'CLOUDSDK_CORE_PROJECT',
145+
'GOOGLE_CLOUD_PROJECT',
146+
};

lib/sdk_labels.json renamed to static/sdk_labels.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"P2",
77
"P3",
88
"P4",
9+
"analyzer-analysis-options",
910
"analyzer-api",
1011
"analyzer-ast-builder",
1112
"analyzer-benchmarks",
@@ -94,6 +95,7 @@
9495
"customer-vm",
9596
"dart-1-removal",
9697
"dart-cli-analyze",
98+
"dart-cli-compile",
9799
"dart-cli-create",
98100
"dart-cli-fix",
99101
"dart-cli-format",
@@ -231,5 +233,6 @@
231233
"web-eval",
232234
"web-js-interop",
233235
"web-libraries",
234-
"web-platform"
235-
]
236+
"web-platform",
237+
"web-techical-debt"
238+
]

tool/update_sdk_labels.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Future main() async {
1717

1818
final labelValues = labels.map((label) => label.name).toList();
1919

20-
File('lib/sdk_labels.json').writeAsStringSync(
20+
File('static/sdk_labels.json').writeAsStringSync(
2121
const JsonEncoder.withIndent(' ').convert(labelValues));
2222
} finally {
2323
gitHub.dispose();

0 commit comments

Comments
 (0)