Skip to content

Commit 42a02ad

Browse files
committed
first commit
0 parents  commit 42a02ad

16 files changed

+613
-0
lines changed

.github/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
**/.DS_Store

.github/_README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Workflow for Managing Pub.dev Packages
2+
3+
## Workflows
4+
5+
**This repository contains GitHub Actions workflows that simplify the management of your pub.dev packages:**
6+
7+
- The `prepare.yaml` workflow triggers on every push to the main branch. It automatically updates the CHANGELOG.md, formats the Dart code, and applies Dart fixes with each push.
8+
- The `publish.yaml` workflow activates upon the creation of a new release, automatically handling the package's publication to pub.dev.
9+
10+
## Setup Instructions
11+
12+
**1. Navigate to your project:**
13+
14+
```zsh
15+
cd your_project
16+
```
17+
18+
**2. Clone this repo into a `.github/` folder:**
19+
20+
```zsh
21+
git clone https://github.com/dev-cetera/pub.dev_package_workflow.git .github
22+
```
23+
24+
**3. Remove the `/.git` folder to include it to your project:**
25+
26+
_On macOS and Linux:_
27+
28+
```zsh
29+
rm -rf .github/.git/
30+
```
31+
32+
_On Windows:_
33+
34+
```cmd
35+
rmdir /s /q .github/.git/
36+
```

.github/scripts/update_changelog.dart

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//.title
2+
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
3+
//
4+
// Dart/Flutter (DF) Packages by dev-cetera.com & contributors. The use of this
5+
// source code is governed by an MIT-style license described in the LICENSE
6+
// file located in this project's root directory.
7+
//
8+
// See: https://opensource.org/license/mit
9+
//
10+
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
11+
//.title~
12+
13+
import 'dart:io';
14+
15+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
16+
17+
void main(List<String> args) {
18+
final version = args.isNotEmpty ? args[0] : '0.1.0';
19+
final comitMesssage = args.length > 1 ? args[1].replaceFirst('+', '') : '';
20+
final changelogPath = 'CHANGELOG.md';
21+
final file = File(changelogPath);
22+
if (!file.existsSync()) {
23+
print('$changelogPath does not exist.');
24+
exit(1);
25+
}
26+
var contents = file.readAsStringSync();
27+
contents = contents.replaceAll('# Changelog', '').trim();
28+
final sections = extractSections(contents);
29+
final versionExist = sections.where((e) => e.version == version).isNotEmpty;
30+
if (versionExist) {
31+
sections.where((e) => e.version == version).forEach((e) {
32+
e.addUpdate(comitMesssage);
33+
});
34+
} else {
35+
sections.add(
36+
_VersionSection(
37+
version: version,
38+
releasedAt: DateTime.now().toUtc(),
39+
updates: {comitMesssage},
40+
),
41+
);
42+
}
43+
contents = '# Changelog\n\n${(sections.toList()..sort((a, b) {
44+
return compareVersions(b.version, a.version);
45+
})).map((e) => e.toString()).join('\n')}';
46+
47+
file.writeAsStringSync(contents);
48+
print('Changelog updated with version $version.');
49+
}
50+
51+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
52+
53+
Set<_VersionSection> extractSections(String contents) {
54+
final headerPattern = RegExp(r'## \[\d+\.\d+\.\d+(\+\d+)?\]');
55+
final allVersionMatches = headerPattern.allMatches(contents).toList();
56+
final results = <_VersionSection>{};
57+
for (var i = 0; i < allVersionMatches.length; i++) {
58+
final start = allVersionMatches[i].end;
59+
final end = i + 1 < allVersionMatches.length ? allVersionMatches[i + 1].start : contents.length;
60+
final sectionContents = contents.substring(start, end).trim();
61+
final lines = sectionContents.split('\n').where((line) => line.isNotEmpty).toList();
62+
final version =
63+
allVersionMatches[i].group(0)!.substring(4, allVersionMatches[i].group(0)!.length - 1);
64+
var releasedAt = DateTime.now().toUtc();
65+
final updates = <String>{};
66+
final old = lines
67+
.map((e) => e.trim())
68+
.where((e) => e.isNotEmpty)
69+
.map((e) => e.startsWith('-') ? e.substring(1) : e)
70+
.map((e) => e.trim())
71+
.where((e) => e.isNotEmpty);
72+
for (var line in old) {
73+
if (line.contains('Released @')) {
74+
releasedAt = parseReleaseDate(line);
75+
} else {
76+
updates.add(line);
77+
}
78+
}
79+
results.add(_VersionSection(
80+
version: version,
81+
releasedAt: releasedAt,
82+
updates: updates,
83+
));
84+
}
85+
86+
return results;
87+
}
88+
89+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
90+
91+
class _VersionSection {
92+
//
93+
//
94+
//
95+
96+
String version;
97+
DateTime releasedAt;
98+
Set<String> updates;
99+
100+
//
101+
//
102+
//
103+
104+
_VersionSection({
105+
required this.version,
106+
required this.releasedAt,
107+
Set<String>? updates,
108+
}) : this.updates = updates ?? {};
109+
110+
//
111+
//
112+
//
113+
114+
void addUpdate(String update) {
115+
updates.add(update);
116+
releasedAt = DateTime.now().toUtc();
117+
}
118+
119+
//
120+
//
121+
//
122+
123+
@override
124+
String toString() {
125+
final updatesString = updates.map((update) => '- $update').join('\n');
126+
return '## [$version]\n\n- Released @ ${releasedAt.month}/${releasedAt.year} (UTC)\n$updatesString\n';
127+
}
128+
}
129+
130+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
131+
132+
int compareVersions(String version1, String version2) {
133+
List<int> parseVersion(String version) {
134+
// Split by the '+' first to handle the build number
135+
final parts = version.split('+');
136+
final versionParts = parts[0].split('.').map(int.tryParse).map((e) => e ?? 0).toList();
137+
// Add the build number as the last part (if it exists)
138+
if (parts.length > 1) {
139+
versionParts.add(int.tryParse(parts[1]) ?? 0);
140+
}
141+
return versionParts;
142+
}
143+
144+
final v1 = parseVersion(version1);
145+
final v2 = parseVersion(version2);
146+
final maxLength = v1.length > v2.length ? v1.length : v2.length;
147+
for (var i = 0; i < maxLength; i++) {
148+
final part1 = i < v1.length ? v1[i] : 0;
149+
final part2 = i < v2.length ? v2[i] : 0;
150+
if (part1 > part2) return 1;
151+
if (part1 < part2) return -1;
152+
}
153+
return 0;
154+
}
155+
156+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
157+
158+
DateTime parseReleaseDate(String line) {
159+
if (line.contains('Released @')) {
160+
final temp = line.split('Released @').last.trim().replaceAll(' (UTC)', '');
161+
final parts = temp.split('/');
162+
if (parts.length == 2) {
163+
final month = int.tryParse(parts[0]) ?? 1;
164+
final year = int.tryParse(parts[1]) ?? DateTime.now().year;
165+
return DateTime.utc(year, month);
166+
}
167+
}
168+
169+
return DateTime.now().toUtc();
170+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
##.title
2+
## ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
3+
##
4+
## Dart/Flutter (DF) Packages by dev-cetera.com & contributors. The use of this
5+
## source code is governed by an MIT-style license described in the LICENSE
6+
## file located in this project's root directory.
7+
##
8+
## See: https://opensource.org/license/mit
9+
##
10+
## ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
11+
##.title~
12+
13+
name: Deploy hosted_example to GitHub Pages
14+
15+
## -----------------------------------------------------------------------------
16+
17+
on:
18+
push:
19+
branches:
20+
- main
21+
22+
## -----------------------------------------------------------------------------
23+
24+
jobs:
25+
build-and-deploy:
26+
runs-on: ubuntu-latest
27+
steps:
28+
# Checkout the code from the repository
29+
- name: Checkout repository
30+
uses: actions/checkout@v4
31+
32+
# Set up Flutter
33+
- name: Set up Flutter
34+
uses: subosito/flutter-action@v2
35+
with:
36+
channel: "stable"
37+
flutter-version: "3.x"
38+
39+
# Install all project dependencies
40+
- name: Install dependencies
41+
run: flutter pub get
42+
working-directory: ./hosted_example
43+
44+
# Enable Flutter web support
45+
- name: Enable Flutter web support
46+
run: flutter config --enable-web
47+
working-directory: ./hosted_example
48+
49+
# Build the web app in /hosted_example/
50+
- name: Build web app in /hosted_example/
51+
run: flutter build web --release --base-href "/${{ github.event.repository.name }}/"
52+
working-directory: ./hosted_example
53+
54+
# Necessary
55+
- name: Create .nojekyll
56+
run: touch hosted_example/build/web/.nojekyll
57+
58+
# Necessary
59+
- name: Create 404.html for SPA routing
60+
run: cp hosted_example/build/web/index.html hosted_example/build/web/404.html
61+
62+
# Deploy the app to GitHub Pages
63+
- name: Deploy to GitHub Pages
64+
uses: peaceiris/actions-gh-pages@v4
65+
with:
66+
github_token: ${{ secrets.GITHUB_TOKEN }}
67+
publish_dir: ./hosted_example/build/web
68+
keep_files: false

.github/workflows/prepare.yml

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
##.title
2+
## ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
3+
##
4+
## Dart/Flutter (DF) Packages by dev-cetera.com & contributors. The use of this
5+
## source code is governed by an MIT-style license described in the LICENSE
6+
## file located in this project's root directory.
7+
##
8+
## See: https://opensource.org/license/mit
9+
##
10+
## ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
11+
##.title~
12+
13+
name: Prepare version
14+
15+
## -----------------------------------------------------------------------------
16+
17+
on:
18+
push:
19+
branches:
20+
- main
21+
22+
## -----------------------------------------------------------------------------
23+
24+
jobs:
25+
prepare:
26+
runs-on: ubuntu-latest
27+
steps:
28+
# Checkout the code from the repository
29+
- name: Checkout code
30+
uses: actions/checkout@v3
31+
32+
# Get the latest commit message
33+
- name: Get commit messages
34+
id: get_commits
35+
run: |
36+
COMMIT_MESSAGES=$(git log --format=%B -n 1 HEAD)
37+
echo "::set-output name=COMMIT_MESSAGES::${COMMIT_MESSAGES}"
38+
39+
# Check if the commit message starts with '+'
40+
- name: Check commit message
41+
id: check_message
42+
run: |
43+
if echo "${{ steps.get_commits.outputs.COMMIT_MESSAGES }}" | grep -q "^+"; then
44+
echo "::set-output name=PROCEED::true"
45+
else
46+
echo "::set-output name=PROCEED::false"
47+
fi
48+
49+
# Debug environment variables
50+
- name: Debug environment variables
51+
run: |
52+
echo "PROCEED=${{ steps.check_message.outputs.PROCEED }}"
53+
echo "COMMIT_MESSAGES=${{ steps.get_commits.outputs.COMMIT_MESSAGES }}"
54+
55+
# Set up Dart if the commit message check passed
56+
- name: Set up Dart
57+
if: ${{ steps.check_message.outputs.PROCEED == 'true' }}
58+
uses: dart-lang/setup-dart@v1.2
59+
60+
# Format Dart code if the commit message check passed
61+
- name: Format Dart code
62+
if: ${{ steps.check_message.outputs.PROCEED == 'true' }}
63+
run: dart format .
64+
65+
# Apply Dart fixes if the commit message check passed
66+
- name: Apply Dart fixes
67+
if: ${{ steps.check_message.outputs.PROCEED == 'true' }}
68+
run: dart fix --apply
69+
70+
# Extract the version from pubspec.yaml if the commit message check passed
71+
- name: Extract version from pubspec.yaml
72+
if: ${{ steps.check_message.outputs.PROCEED == 'true' }}
73+
id: get_version
74+
run: |
75+
VERSION=$(grep "version:" pubspec.yaml | sed 's/version: //')
76+
echo "Version extracted from pubspec.yaml: $VERSION"
77+
echo "::set-output name=PUBSPEC_VERSION::${VERSION}"
78+
79+
# Update CHANGELOG.md if the commit message check passed
80+
- name: Update CHANGELOG.md
81+
if: ${{ steps.check_message.outputs.PROCEED == 'true' }}
82+
run: |
83+
RELEASE_NOTES="${{ steps.get_commits.outputs.COMMIT_MESSAGES }}"
84+
dart run .github/scripts/update_changelog.dart "${{ steps.get_version.outputs.PUBSPEC_VERSION }}" "$RELEASE_NOTES"
85+
86+
# Commit and push changes if the commit message check passed
87+
- name: Commit and push changes
88+
if: ${{ steps.check_message.outputs.PROCEED == 'true' }}
89+
run: |
90+
git config user.name github-actions
91+
git config user.email github-actions@github.com
92+
git add .
93+
git commit -m "Prepare version ${{ steps.get_version.outputs.PUBSPEC_VERSION }}"
94+
git push

0 commit comments

Comments
 (0)