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
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
13 changes: 11 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
push:
branches: [main]
pull_request:
workflow_call:

jobs:
# The workspace contains a Flutter package (packages/insforge_flutter), so the
Expand All @@ -13,12 +14,14 @@ jobs:
analyze-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
channel: stable
- name: Install dependencies
run: flutter pub get
- name: Check formatting
run: dart format --output=none --set-exit-if-changed .
- name: Analyze
run: dart analyze .
- name: Check version is in sync (both packages + version.dart)
Expand All @@ -32,3 +35,9 @@ jobs:
- name: Test insforge_flutter
working-directory: packages/insforge_flutter
run: flutter test
- name: Dry-run publish insforge
working-directory: packages/insforge
run: dart pub publish --dry-run
- name: Dry-run publish insforge_flutter
working-directory: packages/insforge_flutter
run: dart pub publish --dry-run
92 changes: 92 additions & 0 deletions .github/workflows/publish-insforge-flutter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Publish insforge_flutter to pub.dev

on:
push:
tags:
- "insforge_flutter-v[0-9]+.[0-9]+.[0-9]+"
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
- "insforge_flutter-v[0-9]+.[0-9]+.[0-9]+-*"
- 'insforge_flutter-v[0-9]+.[0-9]+.[0-9]+\+*'

jobs:
validate:
name: Validate release
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
stable: ${{ steps.version.outputs.stable }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 # v1.7.2

- name: Validate tag matches package version
id: version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
version="$(sed -n 's/^version:[[:space:]]*//p' packages/insforge_flutter/pubspec.yaml)"
if [ "$RELEASE_TAG" != "insforge_flutter-v$version" ]; then
echo "Tag $RELEASE_TAG does not match insforge_flutter version $version."
exit 1
fi

echo "version=$version" >> "$GITHUB_OUTPUT"
if [[ "$version" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "stable=true" >> "$GITHUB_OUTPUT"
else
echo "stable=false" >> "$GITHUB_OUTPUT"
fi

- name: Check lockstep versions
# Run the dependency-free script directly so Dart does not try to
# resolve the root workspace, which also contains a Flutter package.
run: dart tool/set_version.dart --check

- name: Verify matching insforge version is published
env:
RELEASE_VERSION: ${{ steps.version.outputs.version }}
run: |
if ! curl --fail --silent --show-error --retry 5 --retry-delay 10 \
"https://pub.dev/api/packages/insforge/versions/$RELEASE_VERSION" \
>/dev/null; then
echo "insforge $RELEASE_VERSION is not available on pub.dev yet." >&2
echo "Publish insforge-v$RELEASE_VERSION first and wait for it to become available." >&2
exit 1
fi

quality:
name: Run release quality checks
permissions:
contents: read
uses: ./.github/workflows/ci.yaml

publish:
name: Publish insforge_flutter
needs: [validate, quality]
permissions:
id-token: write
uses: dart-lang/setup-dart/.github/workflows/publish.yml@65eb853c7ba17dde3be364c3d2858773e7144260 # v1.7.2
with:
working-directory: packages/insforge_flutter

release:
name: Create stable GitHub Release
if: needs.validate.outputs.stable == 'true'
needs: [validate, publish]
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.ref_name }}
REPOSITORY: ${{ github.repository }}
run: |
if gh release view "$RELEASE_TAG" --repo "$REPOSITORY" >/dev/null 2>&1; then
echo "GitHub Release $RELEASE_TAG already exists"
else
gh release create "$RELEASE_TAG" --repo "$REPOSITORY" --generate-notes --verify-tag
fi
79 changes: 79 additions & 0 deletions .github/workflows/publish-insforge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Publish insforge to pub.dev

on:
push:
tags:
- "insforge-v[0-9]+.[0-9]+.[0-9]+"
- "insforge-v[0-9]+.[0-9]+.[0-9]+-*"
- 'insforge-v[0-9]+.[0-9]+.[0-9]+\+*'

jobs:
validate:
name: Validate release
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
stable: ${{ steps.version.outputs.stable }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- uses: dart-lang/setup-dart@65eb853c7ba17dde3be364c3d2858773e7144260 # v1.7.2

- name: Validate tag matches package version
id: version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
version="$(sed -n 's/^version:[[:space:]]*//p' packages/insforge/pubspec.yaml)"
if [ "$RELEASE_TAG" != "insforge-v$version" ]; then
echo "Tag $RELEASE_TAG does not match insforge version $version."
exit 1
fi

if [[ "$version" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "stable=true" >> "$GITHUB_OUTPUT"
else
echo "stable=false" >> "$GITHUB_OUTPUT"
fi

- name: Check lockstep versions
# Run the dependency-free script directly so Dart does not try to
# resolve the root workspace, which also contains a Flutter package.
run: dart tool/set_version.dart --check

quality:
name: Run release quality checks
permissions:
contents: read
uses: ./.github/workflows/ci.yaml

publish:
name: Publish insforge
needs: [validate, quality]
permissions:
id-token: write
uses: dart-lang/setup-dart/.github/workflows/publish.yml@65eb853c7ba17dde3be364c3d2858773e7144260 # v1.7.2
with:
working-directory: packages/insforge

release:
name: Create stable GitHub Release
if: needs.validate.outputs.stable == 'true'
needs: [validate, publish]
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.ref_name }}
REPOSITORY: ${{ github.repository }}
run: |
if gh release view "$RELEASE_TAG" --repo "$REPOSITORY" >/dev/null 2>&1; then
echo "GitHub Release $RELEASE_TAG already exists"
else
gh release create "$RELEASE_TAG" --repo "$REPOSITORY" --generate-notes --verify-tag
fi
24 changes: 20 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,28 @@ apart.
1. `dart run tool/set_version.dart <new-version>`.
2. Add a `## <new-version>` section to each package's `CHANGELOG.md`.
3. Commit, open a PR, merge to `main`.
4. Tag the release: `git tag v<new-version> && git push --tags`.
5. Publish **in order** (the Flutter package depends on the Dart one):
4. Tag and publish the core package:
```bash
cd packages/insforge && dart pub publish # wait until it's live
cd ../insforge_flutter && dart pub publish
git tag -a insforge-v<new-version> -m "insforge v<new-version>"
git push origin insforge-v<new-version>
```
5. Wait until that exact `insforge` version is visible on pub.dev.
6. Tag and publish the Flutter package from the same release commit:
```bash
git tag -a insforge_flutter-v<new-version> \
"insforge-v<new-version>^{}" -m "insforge_flutter v<new-version>"
git push origin insforge_flutter-v<new-version>
```

The `^{}` suffix peels the annotated core tag to its commit, so this remains
pinned to the core release even if the checked-out branch advances.

Both tags trigger package-specific GitHub Actions workflows that publish through
pub.dev automated publishing (OIDC). No pub.dev token is stored in GitHub. Do
not push the `insforge_flutter` tag before the matching `insforge` version is
available, because the Flutter package depends on it. Each stable package tag
creates a GitHub Release after pub.dev publication succeeds; prerelease tags
publish to pub.dev without creating a GitHub Release.

> Note on 0.x: under SemVer, `^0.1.0` means `>=0.1.0 <0.2.0`. Bumping the minor
> (e.g. `0.1.x → 0.2.0`) is a breaking change, and the tool updates
Expand Down
45 changes: 33 additions & 12 deletions integration_tests/test/database_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,16 @@ void main() {
final rows = await db
.from(_table)
.insert(<Map<String, dynamic>>[
<String, dynamic>{'name': 'batch-a-$ts', 'value': 'batch', 'score': 20},
<String, dynamic>{'name': 'batch-b-$ts', 'value': 'batch', 'score': 30},
<String, dynamic>{
'name': 'batch-a-$ts',
'value': 'batch',
'score': 20,
},
<String, dynamic>{
'name': 'batch-b-$ts',
'value': 'batch',
'score': 30,
},
])
.select()
.execute();
Expand All @@ -159,7 +167,9 @@ void main() {
final tag = 'update-${DateTime.now().microsecondsSinceEpoch}';
final inserted = await db
.from(_table)
.insert(<String, dynamic>{'name': tag, 'value': 'before', 'score': 0})
.insert(
<String, dynamic>{'name': tag, 'value': 'before', 'score': 0},
)
.select()
.execute();
final Object id = inserted.first['id'] as Object;
Expand Down Expand Up @@ -207,8 +217,7 @@ void main() {

await db.from(_table).eq('id', id).delete().execute();

final check =
await db.from(_table).select('id').eq('id', id).execute();
final check = await db.from(_table).select('id').eq('id', id).execute();
expect(check, isEmpty);
});

Expand All @@ -222,14 +231,27 @@ void main() {
final seeded = await db
.from(_table)
.insert(<Map<String, dynamic>>[
<String, dynamic>{'name': '$tag-alpha', 'value': 'hello world', 'score': 10},
<String, dynamic>{'name': '$tag-beta', 'value': 'hello earth', 'score': 20},
<String, dynamic>{'name': '$tag-gamma', 'value': 'goodbye world', 'score': 30},
<String, dynamic>{
'name': '$tag-alpha',
'value': 'hello world',
'score': 10,
},
<String, dynamic>{
'name': '$tag-beta',
'value': 'hello earth',
'score': 20,
},
<String, dynamic>{
'name': '$tag-gamma',
'value': 'goodbye world',
'score': 30,
},
])
.select()
.execute();
seedIds =
seeded.map((Map<String, dynamic> r) => r['id'] as Object).toList();
seedIds = seeded
.map((Map<String, dynamic> r) => r['id'] as Object)
.toList();
insertedIds.addAll(seedIds);
});

Expand Down Expand Up @@ -310,8 +332,7 @@ void main() {
final rows = await db
.from(_table)
.select('name')
.inFilter('name', <Object>['$tag-alpha', '$tag-gamma'])
.execute();
.inFilter('name', <Object>['$tag-alpha', '$tag-gamma']).execute();
expect(rows.length, greaterThanOrEqualTo(2));
});

Expand Down
9 changes: 5 additions & 4 deletions integration_tests/test/storage_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ void main() {
test('uploadAutoKey returns a server-generated key', () async {
if (!bucketAvailable) return;

final content = 'Auto upload – ${DateTime.now().microsecondsSinceEpoch}';
final content =
'Auto upload – ${DateTime.now().microsecondsSinceEpoch}';
final bytes = Uint8List.fromList(utf8.encode(content));

try {
Expand All @@ -121,9 +122,9 @@ void main() {

test('download of a non-existent object throws', () async {
await expectLater(
storage
.from(_bucket)
.download('nonexistent-${DateTime.now().microsecondsSinceEpoch}.txt'),
storage.from(_bucket).download(
'nonexistent-${DateTime.now().microsecondsSinceEpoch}.txt',
),
throwsA(isA<InsforgeHttpException>()),
);
});
Expand Down
4 changes: 3 additions & 1 deletion packages/insforge/.pubignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Files excluded from the published package archive (not from git).
# Tests aren't needed by consumers and keep the package small.
# Local tooling, build artifacts, and tests aren't needed by consumers.
.dart_tool/
build/
test/
3 changes: 1 addition & 2 deletions packages/insforge/lib/src/ai/images_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ class Images {
if (rawImages is List) {
for (final img in rawImages.whereType<Map<String, dynamic>>()) {
final imageUrl = img['image_url'];
final url =
imageUrl is Map<String, dynamic> ? imageUrl['url'] : null;
final url = imageUrl is Map<String, dynamic> ? imageUrl['url'] : null;
if (url is String && url.isNotEmpty) {
images.add(GeneratedImage.fromImageUrl(url));
}
Expand Down
3 changes: 1 addition & 2 deletions packages/insforge/lib/src/ai/models/chat_completion.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ class ChatCompletionResponse {
final Usage? usage;

/// Convenience accessor for the first choice's text content.
String? get content =>
choices.isEmpty ? null : choices.first.message.content;
String? get content => choices.isEmpty ? null : choices.first.message.content;

factory ChatCompletionResponse.fromJson(Map<String, dynamic> json) {
final rawChoices = json['choices'];
Expand Down
2 changes: 1 addition & 1 deletion packages/insforge/lib/src/ai/models/embeddings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class EmbeddingsRequest {
required this.input,
this.encodingFormat,
this.dimensions,
}) : assert(
}) : assert(
input is String || input is List<String>,
'input must be a String or List<String>',
);
Expand Down
1 change: 0 additions & 1 deletion packages/insforge/lib/src/auth/auth_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'dart:convert';

import 'package:insforge/insforge.dart';


/// Storage key for the persisted refresh token.
const String kRefreshTokenKey = 'insforge_refresh_token';

Expand Down
Loading