diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index dd84ea7..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..74a8876 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,140 @@ +name: ๐Ÿ› Bug Report +description: Report a bug or issue with docx_viewer +title: "[Bug]: " +labels: ["bug", "needs-triage"] +assignees: + - santoshvandari + +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug! Please fill out the form below. + + - type: textarea + id: description + attributes: + label: Bug Description + description: A clear and concise description of what the bug is. + placeholder: Tell us what happened! + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Steps to Reproduce + description: Steps to reproduce the behavior + placeholder: | + 1. Go to '...' + 2. Click on '...' + 3. See error + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: What did you expect to happen? + placeholder: Describe the expected behavior + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual Behavior + description: What actually happened? + placeholder: Describe what actually happened + validations: + required: true + + - type: textarea + id: code + attributes: + label: Code Sample + description: Please provide a minimal code sample that reproduces the issue + placeholder: | + ```dart + DocxView( + filePath: 'path/to/file.docx', + ) + ``` + render: dart + validations: + required: false + + - type: dropdown + id: platform + attributes: + label: Platform + description: Which platform(s) are you experiencing this issue on? + multiple: true + options: + - Android + - iOS + - Web + - Windows + - macOS + - Linux + validations: + required: true + + - type: input + id: flutter-version + attributes: + label: Flutter Version + description: Run `flutter --version` and paste the output + placeholder: "e.g., Flutter 3.24.3" + validations: + required: true + + - type: input + id: dart-version + attributes: + label: Dart Version + description: Your Dart SDK version + placeholder: "e.g., Dart 3.5.4" + validations: + required: true + + - type: input + id: package-version + attributes: + label: docx_viewer Version + description: Which version of docx_viewer are you using? + placeholder: "e.g., 1.0.0" + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Error Logs + description: Please paste any relevant error logs + placeholder: Paste error logs here + render: shell + validations: + required: false + + - type: textarea + id: additional + attributes: + label: Additional Context + description: Add any other context about the problem here + placeholder: Any additional information that might be helpful + validations: + required: false + + - type: checkboxes + id: checklist + attributes: + label: Checklist + options: + - label: I have searched existing issues to make sure this is not a duplicate + required: true + - label: I have provided all required information above + required: true + - label: I am using the latest version of docx_viewer + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..7239cc5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: ๐Ÿ“š Documentation + url: https://pub.dev/packages/docx_viewer + about: Check the package documentation on pub.dev + - name: ๐Ÿ’ฌ Discussions + url: https://github.com/santoshvandari/docx_viewer/discussions + about: Ask questions and discuss with the community + - name: โ“ General Questions + url: https://github.com/santoshvandari/docx_viewer/discussions/new?category=q-a + about: Ask general questions about using docx_viewer diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index bbcbbe7..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..1eabbd9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,115 @@ +name: โœจ Feature Request +description: Suggest a new feature or enhancement for docx_viewer +title: "[Feature]: " +labels: ["enhancement", "needs-triage"] +assignees: + - santoshvandari + +body: + - type: markdown + attributes: + value: | + Thanks for suggesting a feature! Please fill out the form below. + + - type: textarea + id: problem + attributes: + label: Is your feature request related to a problem? + description: A clear and concise description of what the problem is. + placeholder: "I'm always frustrated when..." + validations: + required: false + + - type: textarea + id: solution + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + placeholder: Describe your proposed solution + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + placeholder: Any alternative approaches? + validations: + required: false + + - type: textarea + id: use-case + attributes: + label: Use Case + description: Describe your use case and how this feature would benefit you + placeholder: How would you use this feature? + validations: + required: true + + - type: textarea + id: example + attributes: + label: Code Example + description: If applicable, provide a code example of how you envision using this feature + placeholder: | + ```dart + // Example usage + DocxView( + filePath: 'document.docx', + newFeature: true, + ) + ``` + render: dart + validations: + required: false + + - type: dropdown + id: platform + attributes: + label: Target Platform(s) + description: Which platform(s) should this feature support? + multiple: true + options: + - All platforms + - Android + - iOS + - Web + - Windows + - macOS + - Linux + validations: + required: true + + - type: dropdown + id: priority + attributes: + label: Priority + description: How important is this feature to you? + options: + - Low - Nice to have + - Medium - Would improve my workflow + - High - Blocking my use case + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Additional Context + description: Add any other context, screenshots, or examples about the feature request + placeholder: Any additional information + validations: + required: false + + - type: checkboxes + id: checklist + attributes: + label: Checklist + options: + - label: I have searched existing issues to make sure this is not a duplicate + required: true + - label: This feature would be useful to other users, not just me + required: true + - label: I am willing to help implement this feature + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..0d9ef92 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,76 @@ +## Description + + + +## Type of Change + + + +- [ ] ๐Ÿ› Bug fix (non-breaking change which fixes an issue) +- [ ] โœจ New feature (non-breaking change which adds functionality) +- [ ] ๐Ÿ’ฅ Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] ๐Ÿ“ Documentation update +- [ ] ๐ŸŽจ Style/UI update (no functional changes) +- [ ] โ™ป๏ธ Code refactoring (no functional changes) +- [ ] โšก Performance improvement +- [ ] โœ… Test update +- [ ] ๐Ÿ”ง Configuration change +- [ ] ๐Ÿ”จ Build/CI update + +## Related Issue(s) + + + +Closes # + +## Changes Made + + + +- +- +- + +## Testing + + + +- [ ] Unit tests pass (`flutter test`) +- [ ] Analysis passes (`flutter analyze`) +- [ ] Format check passes (`dart format --set-exit-if-changed .`) +- [ ] Tested on Android +- [ ] Tested on iOS +- [ ] Tested on Web +- [ ] Tested on Windows +- [ ] Tested on macOS +- [ ] Tested on Linux + +### Test Configuration + +**Flutter version:** +**Dart version:** +**Operating System:** + +## Screenshots (if applicable) + + + +## Checklist + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published +- [ ] I have updated the CHANGELOG.md file + +## Additional Notes + + + +## Breaking Changes + + diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000..f18a43d --- /dev/null +++ b/.github/README.md @@ -0,0 +1,243 @@ +# GitHub Actions CI/CD Setup + +This directory contains GitHub Actions workflows and templates for the docx_viewer package. + +## ๐Ÿ“ Contents + +- **workflows/** - Automated CI/CD pipelines + - `ci.yml` - Main CI pipeline (runs on PRs to main/dev) + - `publish.yml` - Manual package publishing workflow + - `quality-check.yml` - Weekly code quality audit + - `auto-label.yml` - Automatic PR/issue labeling + - `welcome.yml` - Welcome message for first-time contributors + - `stale.yml` - Auto-close stale issues/PRs + - `docs.yml` - Documentation coverage check + - `changelog-check.yml` - Ensures CHANGELOG.md is updated + +- **ISSUE_TEMPLATE/** - Issue and PR templates + - `bug_report.yml` - Bug report template + - `feature_request.yml` - Feature request template + - `config.yml` - Issue template configuration + +- **labeler.yml** - Auto-labeling configuration +- **dependabot.yml** - Automated dependency updates +- **PULL_REQUEST_TEMPLATE.md** - Pull request template + +## ๐Ÿš€ CI Workflows + +### 1. CI Pipeline (`ci.yml`) +**Triggers:** Pull requests and pushes to `main` or `dev` branches + +**Jobs:** +- โœ… **Analyze & Format Check** - Code formatting and static analysis +- ๐Ÿงช **Run Tests** - Unit tests with coverage reporting (comments on PR) +- ๐Ÿ“ฆ **Package Analysis** - Validates package quality with `pana` +- ๐Ÿ—๏ธ **Build Example** - Builds on Web and iOS +- ๐Ÿ”’ **Security Scan** - Checks for vulnerabilities +- ๐Ÿ“ **Lint Report** - Posts analysis results as PR comment + +### 2. Publish Workflow (`publish.yml`) +**Triggers:** Manual trigger only + +**Purpose:** Publishes package to pub.dev +- Validates version consistency +- Supports dry-run mode +- Creates Git tags and GitHub releases + +### 3. Quality Check (`quality-check.yml`) +**Triggers:** Weekly (Mondays 9 AM UTC) or manual + +**Purpose:** Weekly code quality audit +- Runs comprehensive analysis +- Generates test coverage reports +- Checks for code smells (TODO/FIXME) +- Creates issues if quality degrades + +### 4. Auto Label (`auto-label.yml`) +**Triggers:** When PR is opened or updated + +**Purpose:** Automatically labels PRs +- Labels based on files changed (lib, docs, ci, etc.) +- Adds size labels (XS, S, M, L, XL) +- Marks breaking changes automatically + +### 5. Welcome (`welcome.yml`) +**Triggers:** First-time issue or PR from new contributors + +**Purpose:** Welcome new contributors +- Friendly welcome message for first-time contributors +- Guidance for next steps +- Tips for successful contribution + +### 6. Stale (`stale.yml`) +**Triggers:** Daily at midnight UTC + +**Purpose:** Keep issues/PRs clean and manageable +- Marks inactive issues (60 days) and PRs (30 days) as stale +- Auto-closes after 14 days (issues) or 7 days (PRs) +- Exempts pinned, security, and work-in-progress items + +### 7. Documentation Check (`docs.yml`) +**Triggers:** PRs that modify Dart files or README + +**Purpose:** Ensure code is well documented +- Generates API documentation +- Checks for undocumented public APIs +- Posts documentation coverage report as PR comment +- Uploads generated docs as artifacts + +### 8. Changelog Check (`changelog-check.yml`) +**Triggers:** PRs to main/dev branches + +**Purpose:** Ensure CHANGELOG.md is updated +- Reminds contributors to update CHANGELOG +- Can skip with `no-changelog` label (for docs-only changes) +- Helps maintain clear project history + +## ๐Ÿ”ง Setup Required + +### Secrets Configuration + +Add these in **Settings โ†’ Secrets and variables โ†’ Actions**: + +1. **`PUB_DEV_CREDENTIALS`** (Required for publishing) + ```bash + # Get credentials + dart pub token add https://pub.dev + + # Linux/macOS + cat ~/.pub-cache/credentials.json + + # Windows + type %APPDATA%\Pub\Cache\credentials.json + ``` + Copy the entire JSON and add as secret. + +### Branch Protection (Recommended) + +**Settings โ†’ Branches โ†’ Add rule** for `main` and `dev`: +- โœ… Require pull request reviews +- โœ… Require status checks (select: Analyze & Format Check, Run Tests) +- โœ… Require branches to be up to date + +## ๐Ÿ“ฆ Publishing New Version + +1. Update `pubspec.yaml` version and `CHANGELOG.md` +2. Commit and push to main +3. Go to **Actions โ†’ Publish to pub.dev โ†’ Run workflow** +4. Enter version number (must match pubspec.yaml) +5. Choose dry-run option for testing +6. Click "Run workflow" + +## ๐Ÿงช Testing Locally + +Before pushing, run these commands: + +```bash +# Format check +dart format . --set-exit-if-changed + +# Analysis +flutter analyze + +# Tests +flutter test --coverage + +# Dry-run publish +flutter pub publish --dry-run + +# Generate documentation +dart doc +``` + +## ๐Ÿ“Š PR Auto-Comments + +The CI automatically posts comments on PRs with: +- ๐Ÿ“Š **Test coverage report** - Overall percentage and per-file breakdown +- ๐Ÿ” **Static analysis results** - Lint warnings and errors +- ๐Ÿ“š **Documentation coverage** - Missing documentation warnings +- ๐Ÿ“ **CHANGELOG reminder** - If CHANGELOG.md not updated + +## ๐Ÿท๏ธ Automatic Labels + +PRs are automatically labeled based on: +- **File changes**: `documentation`, `dependencies`, `ci`, `lib`, `example`, `tests`, `web`, `mobile` +- **PR size**: `size/XS`, `size/S`, `size/M`, `size/L`, `size/XL` +- **Breaking changes**: `breaking-change` (detected from title/description) +- **Changelog**: `changelog-updated` (when CHANGELOG.md is modified) +- **Activity**: `stale` (for inactive issues/PRs) + +## ๐Ÿค– Dependabot + +Automatically creates PRs for: +- Package dependencies (main & example) +- GitHub Actions versions +- Runs weekly on Mondays + +## ๐Ÿ’ก Tips for Contributors + +### Skip CHANGELOG Check +For PRs that don't need changelog entries (docs, CI, typos): +``` +Add label: no-changelog +``` + +### Keep PRs from Being Marked Stale +Add these labels: +- `pinned` - Never mark as stale +- `work-in-progress` - Active development +- `help wanted` - Waiting for contributors + +### Documentation Best Practices +- Add doc comments to all public APIs +- Include code examples in documentation +- Use `///` for public API documentation +- Use `//` for implementation comments + +## ๐Ÿ“ˆ Monitoring + +### Check Workflow Status +- **Actions Tab**: See all workflow runs +- **Pull Requests**: Status checks appear automatically +- **Issues**: Auto-created for quality failures + +### Weekly Reports +- Quality check runs every Monday +- Results available in Actions tab +- Issues created automatically if checks fail + +## ๐Ÿ› ๏ธ Troubleshooting + +### CI Fails: Format Check +```bash +dart format . +git add . +git commit -m "style: format code" +git push +``` + +### CI Fails: Analysis Errors +```bash +flutter analyze +# Fix issues and recommit +``` + +### CI Fails: Tests +```bash +flutter test +# Debug and fix failing tests +``` + +### Stale Bot Closed My PR +Simply add a comment or push new commits to reopen automatically. + +## ๐Ÿ“š Additional Resources + +- [GitHub Actions Documentation](https://docs.github.com/en/actions) +- [Flutter Package Publishing](https://dart.dev/tools/pub/publishing) +- [Conventional Commits](https://www.conventionalcommits.org/) +- [Semantic Versioning](https://semver.org/) + +--- + +**For questions, open an issue or discussion!** ๐Ÿ’ฌ diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1655249 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,49 @@ +version: 2 +updates: + # Enable version updates for pub dependencies + - package-ecosystem: "pub" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 5 + reviewers: + - "santoshvandari" + commit-message: + prefix: "chore(deps)" + prefix-development: "chore(deps-dev)" + labels: + - "dependencies" + - "automated" + + # Enable version updates for pub dependencies in example + - package-ecosystem: "pub" + directory: "/example" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 3 + reviewers: + - "santoshvandari" + commit-message: + prefix: "chore(example-deps)" + labels: + - "dependencies" + - "example" + - "automated" + + # Enable version updates for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 3 + commit-message: + prefix: "chore(ci)" + labels: + - "ci" + - "automated" diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..4317c97 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,51 @@ +# Automatic labeling based on file paths +# Configuration for actions/labeler@v5 +# See: https://github.com/actions/labeler + +documentation: + - changed-files: + - any-glob-to-any-file: '**/*.md' + - changed-files: + - any-glob-to-any-file: 'docs/**/*' + +dependencies: + - changed-files: + - any-glob-to-any-file: 'pubspec.yaml' + - changed-files: + - any-glob-to-any-file: 'example/pubspec.yaml' + +ci: + - changed-files: + - any-glob-to-any-file: '.github/workflows/**' + - changed-files: + - any-glob-to-any-file: '.github/**' + +lib: + - changed-files: + - any-glob-to-any-file: 'lib/**/*' + +example: + - changed-files: + - any-glob-to-any-file: 'example/**/*' + +tests: + - changed-files: + - any-glob-to-any-file: 'test/**/*' + - changed-files: + - any-glob-to-any-file: 'example/test/**/*' + +web: + - changed-files: + - any-glob-to-any-file: 'lib/src/file_io_web.dart' + - changed-files: + - any-glob-to-any-file: '**/*.html' + - changed-files: + - any-glob-to-any-file: '**/*.js' + +mobile: + - changed-files: + - any-glob-to-any-file: 'lib/src/file_io_mobile.dart' + - changed-files: + - any-glob-to-any-file: 'android/**/*' + - changed-files: + - any-glob-to-any-file: 'ios/**/*' diff --git a/.github/workflows/auto-label.yml b/.github/workflows/auto-label.yml new file mode 100644 index 0000000..67f2c2d --- /dev/null +++ b/.github/workflows/auto-label.yml @@ -0,0 +1,78 @@ +name: Auto Label PRs + +on: + pull_request: + types: [opened, edited, synchronize] + +jobs: + label: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Auto label based on files changed + uses: actions/labeler@v5 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/labeler.yml + + - name: Label based on PR size + uses: actions/github-script@v7 + with: + script: | + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const additions = pr.additions; + const deletions = pr.deletions; + const changes = additions + deletions; + + let sizeLabel = ''; + if (changes < 10) sizeLabel = 'size/XS'; + else if (changes < 50) sizeLabel = 'size/S'; + else if (changes < 200) sizeLabel = 'size/M'; + else if (changes < 500) sizeLabel = 'size/L'; + else sizeLabel = 'size/XL'; + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: [sizeLabel] + }); + + - name: Label breaking changes + uses: actions/github-script@v7 + with: + script: | + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const title = pr.title.toLowerCase(); + const body = pr.body ? pr.body.toLowerCase() : ''; + + // Check for breaking change indicators + const hasBreakingChange = title.includes('breaking') || + title.includes('!:') || + body.includes('breaking change') || + body.includes('[x] ๐Ÿ’ฅ breaking change'); + + if (hasBreakingChange) { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['breaking-change'] + }); + } diff --git a/.github/workflows/changelog-check.yml b/.github/workflows/changelog-check.yml new file mode 100644 index 0000000..a01a12d --- /dev/null +++ b/.github/workflows/changelog-check.yml @@ -0,0 +1,101 @@ +name: Changelog Checker + +on: + pull_request: + types: [opened, synchronize, labeled, unlabeled] + branches: + - main + - dev + +jobs: + changelog-check: + name: Check CHANGELOG.md Updated + runs-on: ubuntu-latest + if: "!contains(github.event.pull_request.labels.*.name, 'no-changelog')" + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check if CHANGELOG.md was modified + id: changelog + run: | + # Get list of changed files + git fetch origin ${{ github.base_ref }} + CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) + + echo "Changed files:" + echo "$CHANGED_FILES" + + if echo "$CHANGED_FILES" | grep -q "CHANGELOG.md"; then + echo "changelog_updated=true" >> $GITHUB_OUTPUT + else + echo "changelog_updated=false" >> $GITHUB_OUTPUT + fi + + - name: Comment on PR if CHANGELOG not updated + if: steps.changelog.outputs.changelog_updated == 'false' + uses: actions/github-script@v7 + with: + script: | + const { data: comments } = await github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('๐Ÿ“ CHANGELOG Update Required') + ); + + const message = `## ๐Ÿ“ CHANGELOG Update Required + +โš ๏ธ This PR does not include changes to \`CHANGELOG.md\`. + +**Please update the CHANGELOG with:** +- A brief description of your changes +- The PR number +- Your GitHub username (for attribution) + +**Example:** +\`\`\`markdown +## [Unreleased] + +### Added +- New feature XYZ (#123) by @username + +### Fixed +- Bug ABC (#124) by @username +\`\`\` + +**Note:** If this PR doesn't require a changelog entry (e.g., docs only, CI changes), add the \`no-changelog\` label to skip this check. + +--- +*This is an automated reminder to help maintain project history.*`; + + if (!botComment) { + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: message + }); + } + + - name: Add label if CHANGELOG updated + if: steps.changelog.outputs.changelog_updated == 'true' + uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['changelog-updated'] + }); diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7e2373c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,344 @@ +name: CI + +on: + pull_request: + branches: + - main + - dev + push: + branches: + - main + - dev + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + analyze: + name: Analyze & Format Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Format code + run: dart format . + + - name: Check for formatting changes + id: verify_diff + run: | + if ! git diff --quiet --exit-code; then + echo "formatted=true" >> $GITHUB_OUTPUT + echo "โœจ Code was auto-formatted" + else + echo "formatted=false" >> $GITHUB_OUTPUT + echo "โœ… Code is already properly formatted" + fi + + - name: Commit formatted code + if: steps.verify_diff.outputs.formatted == 'true' && github.event_name == 'pull_request' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "style: auto-format code with dart format [skip ci]" + git push + + - name: Analyze project source + run: flutter analyze --no-fatal-infos + + - name: Check for outdated dependencies + run: flutter pub outdated + + test: + name: Run Tests + runs-on: ubuntu-latest + needs: analyze + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Run tests + run: flutter test --coverage + + - name: Generate coverage report + if: github.event_name == 'pull_request' + run: | + dart pub global activate coverage + dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib + + - name: Generate coverage summary + if: github.event_name == 'pull_request' + run: | + # Install lcov if not available + sudo apt-get update + sudo apt-get install -y lcov + + # Generate summary + lcov --summary coverage/lcov.info > coverage_summary.txt 2>&1 || true + + # Parse coverage percentage + COVERAGE=$(grep -oP 'lines......: \K[0-9.]+' coverage_summary.txt || echo "0") + + # Create a detailed report + echo "## ๐Ÿ“Š Test Coverage Report" > coverage_report.md + echo "" >> coverage_report.md + echo "### Overall Coverage: **${COVERAGE}%**" >> coverage_report.md + echo "" >> coverage_report.md + + # Add coverage badge + if (( $(echo "$COVERAGE >= 80" | bc -l) )); then + echo "![Coverage](https://img.shields.io/badge/coverage-${COVERAGE}%25-brightgreen)" >> coverage_report.md + elif (( $(echo "$COVERAGE >= 60" | bc -l) )); then + echo "![Coverage](https://img.shields.io/badge/coverage-${COVERAGE}%25-yellow)" >> coverage_report.md + else + echo "![Coverage](https://img.shields.io/badge/coverage-${COVERAGE}%25-red)" >> coverage_report.md + fi + + echo "" >> coverage_report.md + echo "### Details" >> coverage_report.md + echo "\`\`\`" >> coverage_report.md + cat coverage_summary.txt >> coverage_report.md + echo "\`\`\`" >> coverage_report.md + + # Generate per-file coverage if available + echo "" >> coverage_report.md + echo "### File Coverage" >> coverage_report.md + echo "\`\`\`" >> coverage_report.md + lcov --list coverage/lcov.info >> coverage_report.md 2>&1 || echo "Detailed file coverage not available" >> coverage_report.md + echo "\`\`\`" >> coverage_report.md + + - name: Comment PR with coverage + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const report = fs.readFileSync('coverage_report.md', 'utf8'); + + // Find existing coverage comment + const { data: comments } = await github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('๐Ÿ“Š Test Coverage Report') + ); + + const commentBody = `${report}\n\n---\n*Updated: ${new Date().toUTCString()}*`; + + if (botComment) { + // Update existing comment + await github.rest.issues.updateComment({ + comment_id: botComment.id, + owner: context.repo.owner, + repo: context.repo.repo, + body: commentBody + }); + } else { + // Create new comment + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: commentBody + }); + } + + - name: Upload coverage artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: | + coverage/ + coverage_report.md + retention-days: 30 + + package-analysis: + name: Package Analysis + runs-on: ubuntu-latest + needs: analyze + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Run package analysis + run: | + dart pub global activate pana + pana --no-warning --exit-code-threshold 0 + + - name: Dry run pub publish + run: flutter pub publish --dry-run + + build-example: + name: Build Example App + runs-on: ${{ matrix.os }} + needs: test + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + include: + - os: ubuntu-latest + build-command: flutter build web + platform: web + - os: macos-latest + build-command: flutter build ios --no-codesign + platform: ios + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies (example) + working-directory: ./example + run: flutter pub get + + - name: Build ${{ matrix.platform }} + working-directory: ./example + run: ${{ matrix.build-command }} + + security-scan: + name: Security Scan + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Check for known security issues + run: | + dart pub global activate dependency_validator + # Ignore references in documentation (README, example mentions) + dependency_validator --ignore=example,file_picker || true + + # Check for actual security vulnerabilities + flutter pub outdated --show-all 2>&1 | grep -i "security\|vulnerability" || echo "No known security issues found" + + lint-report: + name: Generate Lint Report + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Run analyzer and save output + run: | + flutter analyze > analyze_report.txt 2>&1 || true + + # Create formatted report + echo "## ๐Ÿ” Static Analysis Report" > lint_report.md + echo "" >> lint_report.md + echo "\`\`\`" >> lint_report.md + cat analyze_report.txt >> lint_report.md + echo "\`\`\`" >> lint_report.md + + - name: Comment PR with analysis results + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const report = fs.readFileSync('lint_report.md', 'utf8'); + + // Find existing lint comment + const { data: comments } = await github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('๐Ÿ” Static Analysis Report') + ); + + const commentBody = `${report}\n\n---\n*Updated: ${new Date().toUTCString()}*`; + + if (botComment) { + // Update existing comment + await github.rest.issues.updateComment({ + comment_id: botComment.id, + owner: context.repo.owner, + repo: context.repo.repo, + body: commentBody + }); + } else { + // Create new comment + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: commentBody + }); + } diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..9704e93 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,100 @@ +name: Check Documentation + +on: + pull_request: + paths: + - 'lib/**/*.dart' + - 'README.md' + workflow_dispatch: + +jobs: + doc-check: + name: Documentation Check + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Generate documentation + run: | + dart doc --output docs/api + echo "Documentation generated successfully!" + + - name: Check for undocumented public APIs + run: | + echo "## ๐Ÿ“š Documentation Coverage" > doc_report.md + echo "" >> doc_report.md + + # Check for missing documentation + flutter analyze --no-fatal-infos 2>&1 | grep -i "document" > doc_warnings.txt || true + + if [ -s doc_warnings.txt ]; then + echo "โš ๏ธ **Found undocumented APIs:**" >> doc_report.md + echo "\`\`\`" >> doc_report.md + cat doc_warnings.txt >> doc_report.md + echo "\`\`\`" >> doc_report.md + else + echo "โœ… **All public APIs are documented!**" >> doc_report.md + fi + + echo "" >> doc_report.md + echo "---" >> doc_report.md + echo "*Tip: Add documentation comments to all public classes, methods, and functions.*" >> doc_report.md + + - name: Comment PR with documentation report + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const report = fs.readFileSync('doc_report.md', 'utf8'); + + const { data: comments } = await github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('๐Ÿ“š Documentation Coverage') + ); + + const commentBody = `${report}\n\n---\n*Updated: ${new Date().toUTCString()}*`; + + if (botComment) { + await github.rest.issues.updateComment({ + comment_id: botComment.id, + owner: context.repo.owner, + repo: context.repo.repo, + body: commentBody + }); + } else { + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: commentBody + }); + } + + - name: Upload documentation + uses: actions/upload-artifact@v4 + with: + name: api-documentation + path: docs/api + retention-days: 30 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..5357fc2 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,154 @@ +name: Publish to pub.dev + +on: + workflow_dispatch: + inputs: + version: + description: 'Package version to publish (e.g., 1.0.1)' + required: true + type: string + dry-run: + description: 'Perform a dry run without publishing' + required: false + type: boolean + default: false + +jobs: + validate: + name: Validate Package + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Verify formatting + run: dart format --set-exit-if-changed . + + - name: Analyze project source + run: flutter analyze --fatal-infos + + - name: Run tests + run: flutter test + + - name: Check package version + run: | + PUBSPEC_VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //') + INPUT_VERSION="${{ github.event.inputs.version }}" + + if [ "$PUBSPEC_VERSION" != "$INPUT_VERSION" ]; then + echo "โŒ Version mismatch!" + echo "pubspec.yaml version: $PUBSPEC_VERSION" + echo "Input version: $INPUT_VERSION" + echo "" + echo "Please update the version in pubspec.yaml to match: $INPUT_VERSION" + exit 1 + fi + + echo "โœ… Version check passed: $PUBSPEC_VERSION" + + - name: Run package analysis + run: | + dart pub global activate pana + pana --no-warning --exit-code-threshold 0 + + publish: + name: Publish Package + runs-on: ubuntu-latest + needs: validate + if: ${{ !github.event.inputs.dry-run }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Setup pub credentials + run: | + mkdir -p ~/.pub-cache + echo '${{ secrets.PUB_DEV_CREDENTIALS }}' > ~/.pub-cache/credentials.json + + - name: Publish to pub.dev + run: flutter pub publish --force + + - name: Create Git Tag + run: | + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git tag -a v${{ github.event.inputs.version }} -m "Release version ${{ github.event.inputs.version }}" + git push origin v${{ github.event.inputs.version }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create GitHub Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ github.event.inputs.version }} + release_name: Release v${{ github.event.inputs.version }} + body: | + ## ๐Ÿ“ฆ Package Published + + Version **${{ github.event.inputs.version }}** has been published to pub.dev. + + ### Installation + ```yaml + dependencies: + docx_viewer: ^${{ github.event.inputs.version }} + ``` + + ### Links + - [pub.dev](https://pub.dev/packages/docx_viewer) + - [Changelog](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) + draft: false + prerelease: false + + dry-run: + name: Dry Run Publish + runs-on: ubuntu-latest + needs: validate + if: ${{ github.event.inputs.dry-run }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Dry run pub publish + run: flutter pub publish --dry-run + + - name: Dry run summary + run: | + echo "โœ… Dry run completed successfully!" + echo "The package is ready to be published." + echo "" + echo "To publish for real, run this workflow again with 'dry-run' set to false." diff --git a/.github/workflows/quality-check.yml b/.github/workflows/quality-check.yml new file mode 100644 index 0000000..455d502 --- /dev/null +++ b/.github/workflows/quality-check.yml @@ -0,0 +1,116 @@ +name: Code Quality Check + +on: + schedule: + # Run every Monday at 9:00 AM UTC + - cron: '0 9 * * 1' + workflow_dispatch: + +jobs: + quality-check: + name: Weekly Code Quality Audit + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.27.0' + channel: 'stable' + cache: true + + - name: Get dependencies + run: flutter pub get + + - name: Run static analysis + run: flutter analyze --no-fatal-infos > analysis.txt 2>&1 || true + + - name: Run tests with coverage + run: flutter test --coverage + + - name: Check test coverage + run: | + # Install lcov + sudo apt-get update + sudo apt-get install -y lcov + + # Generate coverage report + dart pub global activate coverage + dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib + + # Calculate coverage percentage + COVERAGE=$(lcov --summary coverage/lcov.info 2>&1 | grep "lines" | awk '{print $2}' || echo "0.0%") + echo "Current test coverage: $COVERAGE" + + # You can set a minimum coverage threshold here + # THRESHOLD=80 + # if (( $(echo "$COVERAGE < $THRESHOLD" | bc -l) )); then + # echo "Coverage $COVERAGE% is below threshold $THRESHOLD%" + # exit 1 + # fi + + - name: Check for code smells + run: | + # Check for TODO comments + echo "## TODO Comments Found:" + grep -r "TODO" lib/ || echo "No TODO comments found" + + # Check for FIXME comments + echo "## FIXME Comments Found:" + grep -r "FIXME" lib/ || echo "No FIXME comments found" + + # Check for deprecated API usage + echo "## Checking for deprecated API usage..." + flutter analyze --no-fatal-infos | grep -i "deprecated" || echo "No deprecated API usage found" + + - name: Check dependencies + run: | + echo "## Checking for outdated dependencies..." + flutter pub outdated + + - name: Generate report + if: always() + run: | + echo "# Code Quality Report - $(date)" > quality_report.md + echo "" >> quality_report.md + echo "## Static Analysis" >> quality_report.md + cat analysis.txt >> quality_report.md || echo "No analysis output" >> quality_report.md + echo "" >> quality_report.md + echo "## Dependencies" >> quality_report.md + flutter pub outdated >> quality_report.md || echo "All dependencies up to date" >> quality_report.md + + - name: Upload quality report + if: always() + uses: actions/upload-artifact@v4 + with: + name: quality-report + path: quality_report.md + retention-days: 30 + + - name: Create issue if quality check fails + if: failure() + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + let report = 'No report available'; + + try { + report = fs.readFileSync('quality_report.md', 'utf8'); + } catch (e) { + console.log('Could not read quality report'); + } + + github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: 'โš ๏ธ Weekly Code Quality Check Failed', + body: `The weekly code quality check has detected issues.\n\n${report}`, + labels: ['quality', 'automated'] + }); diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml new file mode 100644 index 0000000..8cf6353 --- /dev/null +++ b/.github/workflows/release-notes.yml @@ -0,0 +1,101 @@ +name: Release Notes Generator + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: 'Tag to generate release notes for' + required: true + type: string + +jobs: + generate-release-notes: + name: Generate Release Notes + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get version info + id: version + run: | + if [ "${{ github.event_name }}" == "release" ]; then + TAG="${{ github.event.release.tag_name }}" + else + TAG="${{ github.event.inputs.tag }}" + fi + + VERSION=${TAG#v} + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Extract changelog for version + id: changelog + run: | + # Extract the section for this version from CHANGELOG.md + VERSION="${{ steps.version.outputs.version }}" + + awk "/## \[$VERSION\]/,/## \[/" CHANGELOG.md | head -n -1 > release_notes.md + + if [ ! -s release_notes.md ]; then + echo "## What's Changed" > release_notes.md + echo "" >> release_notes.md + echo "See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details." >> release_notes.md + fi + + - name: Generate contributor list + run: | + # Get previous tag + PREV_TAG=$(git describe --tags --abbrev=0 ${{ steps.version.outputs.tag }}^ 2>/dev/null || git rev-list --max-parents=0 HEAD) + + echo "" >> release_notes.md + echo "## ๐Ÿ‘ฅ Contributors" >> release_notes.md + echo "" >> release_notes.md + + # Get list of contributors + git log $PREV_TAG..${{ steps.version.outputs.tag }} --format='%aN' | sort -u | while read name; do + echo "- @$name" >> release_notes.md + done + + - name: Add installation instructions + run: | + echo "" >> release_notes.md + echo "## ๐Ÿ“ฆ Installation" >> release_notes.md + echo "" >> release_notes.md + echo '```yaml' >> release_notes.md + echo 'dependencies:' >> release_notes.md + echo " docx_viewer: ^${{ steps.version.outputs.version }}" >> release_notes.md + echo '```' >> release_notes.md + echo "" >> release_notes.md + echo "## ๐Ÿ“š Documentation" >> release_notes.md + echo "" >> release_notes.md + echo "- [pub.dev](https://pub.dev/packages/docx_viewer)" >> release_notes.md + echo "- [API Docs](https://pub.dev/documentation/docx_viewer/latest/)" >> release_notes.md + echo "- [README](https://github.com/${{ github.repository }}/blob/main/README.md)" >> release_notes.md + + - name: Update release notes + if: github.event_name == 'release' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const releaseNotes = fs.readFileSync('release_notes.md', 'utf8'); + + await github.rest.repos.updateRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: context.payload.release.id, + body: releaseNotes + }); + + - name: Upload release notes artifact + uses: actions/upload-artifact@v4 + with: + name: release-notes-${{ steps.version.outputs.version }} + path: release_notes.md + retention-days: 90 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..c07c290 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,60 @@ +name: Stale Issues & PRs + +on: + schedule: + - cron: '0 0 * * *' # Daily at midnight + workflow_dispatch: + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - name: Mark stale issues and PRs + uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + # Issues config + stale-issue-message: | + ๐Ÿ‘‹ This issue has been automatically marked as stale because it has not had recent activity. + + It will be closed in **14 days** if no further activity occurs. + + If this is still relevant, please: + - Add a comment to keep it open + - Provide additional context or updates + + Thank you for your contribution! ๐Ÿ™ + close-issue-message: | + This issue was automatically closed due to inactivity. + + If you believe this is still relevant, please feel free to reopen it with additional context. + stale-issue-label: 'stale' + days-before-issue-stale: 60 + days-before-issue-close: 14 + exempt-issue-labels: 'pinned,security,help wanted,good first issue,enhancement' + + # PRs config + stale-pr-message: | + ๐Ÿ‘‹ This pull request has been automatically marked as stale because it has not had recent activity. + + It will be closed in **7 days** if no further activity occurs. + + To keep this PR open, please: + - Push new commits + - Respond to review comments + - Add a comment explaining the status + + Thank you for your contribution! ๐Ÿ™ + close-pr-message: | + This pull request was automatically closed due to inactivity. + + Feel free to reopen it when you're ready to continue working on it. + stale-pr-label: 'stale' + days-before-pr-stale: 30 + days-before-pr-close: 7 + exempt-pr-labels: 'pinned,security,work-in-progress' diff --git a/.github/workflows/welcome.yml b/.github/workflows/welcome.yml new file mode 100644 index 0000000..cebac26 --- /dev/null +++ b/.github/workflows/welcome.yml @@ -0,0 +1,89 @@ +name: Welcome Contributors + +on: + issues: + types: [opened] + pull_request_target: + types: [opened] + +jobs: + welcome: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - name: Welcome first-time contributors + uses: actions/github-script@v7 + with: + script: | + const isIssue = context.eventName === 'issues'; + const number = context.issue.number; + + // Get all issues/PRs by this author + const { data: items } = isIssue + ? await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + creator: context.actor, + state: 'all' + }) + : await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'all' + }); + + // Check if this is the first contribution + const isFirstTime = items.length === 1; + + if (isFirstTime) { + const message = isIssue + ? `๐Ÿ‘‹ **Welcome @${context.actor}!** + +Thank you for opening your first issue in the docx_viewer package! + +We appreciate you taking the time to report this. A maintainer will review your issue soon. + +Meanwhile, please make sure you've provided: +- โœ… Clear description of the issue +- โœ… Steps to reproduce +- โœ… Flutter & Dart versions +- โœ… Platform(s) affected + +Feel free to join our [discussions](https://github.com/${context.repo.owner}/${context.repo.repo}/discussions) if you have questions!` + : `๐ŸŽ‰ **Congratulations @${context.actor}!** + +Thank you for opening your first pull request! We're excited to review your contribution. + +**What happens next?** +- โœ… CI checks will run automatically +- ๐Ÿ“Š Coverage and lint reports will be posted +- ๐Ÿ‘€ A maintainer will review your changes +- ๐Ÿ’ฌ We may suggest some changes or ask questions + +**Tips:** +- Make sure all CI checks pass +- Keep your branch up to date with main +- Be patient - reviews may take a few days + +Thank you for contributing to docx_viewer! ๐Ÿš€`; + + if (isIssue) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: number, + body: message + }); + } else { + await github.rest.pulls.createReview({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: number, + body: message, + event: 'COMMENT' + }); + } + } diff --git a/example/lib/main.dart b/example/lib/main.dart index 267d524..1a81ddb 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -137,9 +137,8 @@ class _DocumentViewerPageState extends State { 'Running on: ${kIsWeb ? "Web Platform" : "Mobile/Desktop Platform"}', style: TextStyle( fontWeight: FontWeight.bold, - color: kIsWeb - ? Colors.blue.shade900 - : Colors.green.shade900, + color: + kIsWeb ? Colors.blue.shade900 : Colors.green.shade900, ), ), ], diff --git a/example/pubspec.lock b/example/pubspec.lock index e90d93b..c9cfb5d 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -13,42 +13,42 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.19.0" cross_file: dependency: transitive description: @@ -79,15 +79,15 @@ packages: path: ".." relative: true source: path - version: "0.2.2" + version: "1.0.0" fake_async: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.1" ffi: dependency: transitive description: @@ -155,18 +155,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -187,10 +187,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -203,18 +203,18 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" path: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" petitparser: dependency: transitive description: @@ -248,50 +248,50 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.12.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.3.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.3" typed_data: dependency: transitive description: @@ -312,10 +312,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "14.3.0" web: dependency: transitive description: @@ -341,5 +341,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.5.4 <4.0.0" flutter: ">=3.24.0" diff --git a/lib/src/docx_view.dart b/lib/src/docx_view.dart index 39b5e67..7141762 100644 --- a/lib/src/docx_view.dart +++ b/lib/src/docx_view.dart @@ -36,7 +36,7 @@ import 'file_io_stub.dart' /// ``` class DocxView extends StatefulWidget { final String? - filePath; // The path to the DOCX file or URL (local paths not supported on web) + filePath; // The path to the DOCX file or URL (local paths not supported on web) @Deprecated( 'Use filePath or bytes instead. This parameter will be removed in a future version.', ) @@ -81,6 +81,7 @@ class _DocxViewState extends State { Future _validateAndLoadDocxContent() async { // Check if any input is provided if ((widget.filePath == null || widget.filePath!.isEmpty) && + // ignore: deprecated_member_use_from_same_package widget.file == null && widget.bytes == null) { _handleError( @@ -93,7 +94,9 @@ class _DocxViewState extends State { // Ensure that only one of the parameters is provided if (((widget.bytes != null) && + // ignore: deprecated_member_use_from_same_package (widget.file != null || widget.filePath != null)) || + // ignore: deprecated_member_use_from_same_package ((widget.file != null) && (widget.filePath != null))) { _handleError(Exception("Define only one of: filePath, file, or bytes")); return; @@ -125,6 +128,7 @@ class _DocxViewState extends State { } // Load from local file path (mobile/desktop only) + // ignore: deprecated_member_use_from_same_package if (widget.file != null) { // Deprecated file parameter _handleError( diff --git a/lib/src/extract_text_from_docx.dart b/lib/src/extract_text_from_docx.dart index a95f21c..1905486 100644 --- a/lib/src/extract_text_from_docx.dart +++ b/lib/src/extract_text_from_docx.dart @@ -39,10 +39,8 @@ String extractTextFromDocxBytes(Uint8List bytes) { for (final paragraph in document.findAllElements('w:p')) { // Extract and join all text nodes within the paragraph - final textContent = paragraph - .findAllElements('w:t') - .map((node) => node.innerText) - .join(); + final textContent = + paragraph.findAllElements('w:t').map((node) => node.innerText).join(); // Check for numbering information in the paragraph final numIdNode = paragraph.findElements('w:numId').firstOrNull; @@ -57,9 +55,8 @@ String extractTextFromDocxBytes(Uint8List bytes) { } // Add numbering if applicable - final formattedText = (numId != null) - ? '$number. $textContent' - : textContent; + final formattedText = + (numId != null) ? '$number. $textContent' : textContent; extractedText.add(formattedText); } diff --git a/pubspec.yaml b/pubspec.yaml index 21978d3..cdf75ff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,18 +13,18 @@ platforms: macos: linux: environment: - sdk: ^3.5.4 + sdk: '>=3.5.0 <4.0.0' flutter: '>=3.10.0' dependencies: flutter: sdk: flutter - archive: any - xml: any + archive: ^4.0.0 + xml: ^6.5.0 http: ^1.2.2 dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^4.0.0 -