-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3059575
commit 0aa2192
Showing
10 changed files
with
369 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# https://dart.dev/guides/libraries/private-files | ||
# Created by `dart pub` | ||
.dart_tool/ | ||
|
||
# Avoid committing pubspec.lock for library packages; see | ||
# https://dart.dev/guides/libraries/private-files#pubspeclock. | ||
pubspec.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Google cloud run support | ||
|
||
Add helper to build and deploy to Google Cloud Run | ||
|
||
## Setup | ||
|
||
```yaml | ||
dependencies: | ||
tekartik_gcr_build: | ||
git: | ||
url: https://github.com/tekartik/app_build.dart | ||
ref: dart3a | ||
path: packages/gcr_build | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# This file configures the static analysis results for your project (errors, | ||
# warnings, and lints). | ||
# | ||
# This enables the 'recommended' set of lints from `package:lints`. | ||
# This set helps identify many issues that may lead to problems when running | ||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic | ||
# style and format. | ||
# | ||
# If you want a smaller set of lints you can change this to specify | ||
# 'package:lints/core.yaml'. These are just the most critical lints | ||
# (the recommended set includes the core lints). | ||
# The core lints are also what is used by pub.dev for scoring packages. | ||
|
||
include: package:tekartik_lints/package.yaml | ||
|
||
# Uncomment the following section to specify additional rules. | ||
|
||
# linter: | ||
# rules: | ||
# - camel_case_types | ||
|
||
# analyzer: | ||
# exclude: | ||
# - path/to/excluded/files/** | ||
|
||
# For more information about the core and recommended set of lints, see | ||
# https://dart.dev/go/core-lints | ||
|
||
# For additional information about configuring this file, see | ||
# https://dart.dev/guides/language/analysis-options |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
library; | ||
|
||
export 'src/docker.dart' show dockerKillAll; | ||
export 'src/gcr_artifact_repository.dart' show GcrArtifactRepository; | ||
export 'src/gcr_project.dart' show GcrProject, GcrProjectExt; | ||
export 'src/gcr_project_options.dart' show GcrProjectOptions, gcrRegionBelgium; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import 'package:dev_build/shell.dart'; | ||
import 'package:process_run/stdio.dart'; | ||
|
||
/// Kill all running instances | ||
Future<void> dockerKillAll() async { | ||
var processIds = (await run(r''' | ||
docker ps -q | ||
''')).outLines.map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); | ||
if (processIds.isEmpty) { | ||
stdout.writeln('No processIds found'); | ||
return; | ||
} | ||
stdout.writeln('processIds: $processIds'); | ||
await run(''' | ||
docker kill ${processIds.join(' ')} | ||
'''); | ||
} | ||
|
||
/// Get any running process ids | ||
Future<List<String>> dockerComposeGetRunningProcessIds() async { | ||
var processIds = (await run(r''' | ||
docker compose ps -q | ||
''')).outLines.map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); | ||
return processIds; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// { | ||
// 'cleanupPolicyDryRun': true, | ||
// 'createTime': '2022-11-13T15:57:13.330908Z', | ||
// 'description': | ||
// 'This repository is created and used by Cloud Functions for storing function docker images.', | ||
// 'format': 'DOCKER', | ||
// 'labels': {'goog-managed-by': 'cloudfunctions'}, | ||
// 'mode': 'STANDARD_REPOSITORY', | ||
// 'name': 'projects/my_project/locations/my_region/repositories/my_repo', | ||
// 'satisfiesPzi': true, | ||
// 'sizeBytes': '550708161', | ||
// 'updateTime': '2024-10-04T18:24:41.972377Z' | ||
// }; | ||
/// Google cloud artifact repository | ||
class GcrArtifactRepository { | ||
/// Name | ||
final String name; | ||
|
||
List<String> get _parts => name.split('/'); | ||
|
||
/// String repo | ||
String get repository => _parts.last; | ||
|
||
/// String region | ||
String get region => _parts[3]; | ||
|
||
/// String project | ||
String get project => _parts[1]; | ||
|
||
/// Constructor | ||
GcrArtifactRepository.fromJson(Map map) : name = map['name'] as String; | ||
|
||
@override | ||
String toString() => name; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import 'package:dev_build/shell.dart'; | ||
import 'package:process_run/stdio.dart'; | ||
import 'package:tekartik_common_build/version_io.dart'; | ||
import 'package:tekartik_common_utils/common_utils_import.dart'; | ||
import 'package:tekartik_gcr_build/gcr.dart'; | ||
import 'package:tekartik_gcr_build/src/docker.dart'; | ||
|
||
var _debug = false; // devWarning(true); | ||
void _log(Object? message) { | ||
if (_debug) { | ||
// ignore: avoid_print | ||
print(message); | ||
} | ||
} | ||
|
||
/// Google cloud project | ||
class GcrProject { | ||
/// If verbose logging is done | ||
final bool verbose; | ||
|
||
/// Path to the project | ||
final String path; | ||
|
||
/// Options | ||
final GcrProjectOptions options; | ||
|
||
/// Constructor | ||
GcrProject({this.path = '.', required this.options, bool? verbose}) | ||
: verbose = verbose ?? false; | ||
} | ||
|
||
/// Helpers | ||
extension GcrProjectExt on GcrProject { | ||
Shell get _shell => | ||
Shell(workingDirectory: path, verbose: verbose, commandVerbose: true); | ||
|
||
/// Configure docker auth, need after login once per region | ||
Future<void> configureDockerAuth() async { | ||
await _shell.run(''' | ||
gcloud auth configure-docker ${options.region}-docker.pkg.dev | ||
'''); | ||
} | ||
|
||
/// Tag the image before pushing | ||
Future<void> dockerTagImage() async { | ||
await _shell.run(''' | ||
docker tag ${options.image} \\ | ||
${options.region}-docker.pkg.dev/${options.projectId}/${options.name}/${options.image} | ||
'''); | ||
} | ||
|
||
/// Rebuild and run | ||
Future<void> buildAndRun() async { | ||
try { | ||
await kill(); | ||
} catch (_) {} | ||
if (_debug) { | ||
_log('Before running'); | ||
} | ||
try { | ||
await _shell.cloneWithOptions(_shell.options.clone(verbose: true)).run(''' | ||
docker compose up --build --pull never | ||
'''); | ||
if (_debug) { | ||
_log('After running'); | ||
} | ||
} finally { | ||
if (_debug) { | ||
_log('Finally running'); | ||
} | ||
} | ||
} | ||
|
||
/// Rebuild and run | ||
Future<void> build() async { | ||
await _shell.run(''' | ||
docker compose build | ||
'''); | ||
} | ||
|
||
/// Full build and deploy | ||
Future<void> buildAndDeploy() async { | ||
var futures = [ | ||
shellStdioLinesGrouper.runZoned(() async { | ||
await generateVersion(); | ||
await build(); | ||
await dockerTagImage(); | ||
}), | ||
shellStdioLinesGrouper.runZoned(() async { | ||
await configureDockerAuth(); | ||
await createArtifactRepository(); | ||
}) | ||
]; | ||
await Future.wait(futures); | ||
await dockerPush(); | ||
await deploy(); | ||
} | ||
|
||
/// List existing artifact repositories | ||
Future<List<GcrArtifactRepository>> listArtifactRepositories() async { | ||
var result = await _shell.run(''' | ||
gcloud artifacts repositories list --project=${options.projectId} --format json | ||
'''); | ||
var text = result.outText; | ||
var list = jsonDecode(text.trim()) as List; | ||
return list | ||
.map((item) => GcrArtifactRepository.fromJson(item as Map)) | ||
.toList(); | ||
} | ||
|
||
/// Create artifact repository | ||
Future<void> createArtifactRepository({bool? force}) async { | ||
force ??= false; | ||
if (!force) { | ||
var list = await listArtifactRepositories(); | ||
if (list.map((e) => e.repository).contains(options.name)) { | ||
stdout.writeln('Artifact repository "${options.name}" already exists'); | ||
return; | ||
} | ||
} | ||
await _shell.run(''' | ||
gcloud artifacts repositories create ${options.name} --repository-format=docker \\ | ||
--location=${options.region} \\ | ||
${options.description == null ? '' : '--description ${shellArgument(options.description!)}'} \\ | ||
--project=${options.projectId} | ||
'''); | ||
} | ||
|
||
/// Terminate/kill | ||
Future<void> kill() async { | ||
var processIds = await dockerComposeGetRunningProcessIds(); | ||
if (processIds.isNotEmpty) { | ||
await _shell.run(''' | ||
docker compose kill | ||
'''); | ||
} | ||
} | ||
|
||
String get _dockerImage => | ||
'${options.region}-docker.pkg.dev/${options.projectId}/${options.name}/${options.image}'; | ||
|
||
/// Push the docker image | ||
Future<void> dockerPush() async { | ||
await _shell.run(''' | ||
docker push ${shellArgument(_dockerImage)} | ||
'''); | ||
} | ||
|
||
/// Deploy the docker image (must be pushed first) | ||
Future<void> deploy() async { | ||
await _shell.cloneWithOptions(_shell.options.clone(verbose: true)).run(''' | ||
gcloud run deploy ${options.serviceName} \\ | ||
--region ${options.region} \\ | ||
--project ${options.projectId} \\ | ||
--image ${shellArgument(_dockerImage)} \\ | ||
${options.memory == null ? '' : '--memory ${options.memory}'} \\ | ||
--allow-unauthenticated | ||
'''); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/// Google cloud region | ||
var gcrRegionBelgium = 'europe-west1'; | ||
|
||
/// Google cloud project id | ||
class GcrProjectOptions { | ||
/// Google cloud project id | ||
final String projectId; | ||
|
||
/// Region | ||
final String region; | ||
|
||
/// Artifact (repo) name, unique per project | ||
/// Names may only contain lowercase letters, numbers, and hyphens, and must begin with a letter and end with a letter | ||
final String name; | ||
|
||
/// Image name, unique per artifact | ||
/// Must match the image name in the docker-compose file | ||
final String image; | ||
|
||
/// Deployed service name (exported by the project as part of the url of the service) | ||
/// The name must use only lowercase alphanumeric characters and dashes, cannot begin or end with a dash, and cannot be longer than 63 characters. | ||
final String serviceName; | ||
|
||
/// Description | ||
final String? description; | ||
|
||
/// Memory 1G, 2G, 4G, 8G, 16G, 512MB (default) | ||
final String? memory; | ||
|
||
/// Constructor | ||
GcrProjectOptions( | ||
{required this.projectId, | ||
required this.region, | ||
required this.name, | ||
required this.image, | ||
this.description, | ||
required this.serviceName, | ||
this.memory}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
name: tekartik_gcr_build | ||
description: Google Cloud Run experiment | ||
version: 1.0.0 | ||
publish_to: none | ||
|
||
environment: | ||
sdk: ^3.5.0 | ||
|
||
# Add regular dependencies here. | ||
dependencies: | ||
path: | ||
tekartik_common_utils: | ||
git: | ||
url: https://github.com/tekartik/common_utils.dart | ||
ref: dart3a | ||
dev_build: ">=1.0.1" | ||
process_run: ">=1.2.1" | ||
tekartik_common_build: | ||
git: | ||
url: https://github.com/tekartik/app_build.dart | ||
ref: dart3a | ||
path: packages/common_build | ||
|
||
dev_dependencies: | ||
lints: ">=5.0.0" | ||
test: ">=1.24.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Example showing how to use the JsonCodable macro | ||
import 'package:tekartik_gcr_build/gcr.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
void main() { | ||
test('GcrArtifactRepository', () { | ||
var artifactRepositoryMap = { | ||
'cleanupPolicyDryRun': true, | ||
'createTime': '2022-11-13T15:57:13.330908Z', | ||
'description': | ||
'This repository is created and used by Cloud Functions for storing function docker images.', | ||
'format': 'DOCKER', | ||
'labels': {'goog-managed-by': 'cloudfunctions'}, | ||
'mode': 'STANDARD_REPOSITORY', | ||
'name': 'projects/my_project/locations/my_region/repositories/my_repo', | ||
'satisfiesPzi': true, | ||
'sizeBytes': '550708161', | ||
'updateTime': '2024-10-04T18:24:41.972377Z' | ||
}; | ||
var repo = GcrArtifactRepository.fromJson(artifactRepositoryMap); | ||
expect(repo.name, | ||
'projects/my_project/locations/my_region/repositories/my_repo'); | ||
expect(repo.project, 'my_project'); | ||
expect(repo.region, 'my_region'); | ||
expect(repo.repository, 'my_repo'); | ||
}); | ||
} |