From df1650a4feb1030ae098381c80aca8ab33b0c90c Mon Sep 17 00:00:00 2001 From: Yogendra Shelke <25844542+YogendraShelke@users.noreply.github.com> Date: Wed, 3 Dec 2025 18:38:39 +0530 Subject: [PATCH 1/4] chore: automate release process --- .github/actions/setup/action.yml | 27 ---- .github/workflows/ci.yml | 154 ------------------ .github/workflows/release.yml | 75 +++++++++ .github/workflows/secret-scan.yml | 34 ---- .gitignore | 1 + .gitleaks.toml | 71 -------- CHANGELOG.md | 13 ++ CONTRIBUTING.md | 12 +- README.md | 260 ++++++++++++++++++++++++++++++ 9 files changed, 350 insertions(+), 297 deletions(-) delete mode 100644 .github/actions/setup/action.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml delete mode 100644 .github/workflows/secret-scan.yml delete mode 100644 .gitleaks.toml create mode 100644 CHANGELOG.md diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml deleted file mode 100644 index fb98c79..0000000 --- a/.github/actions/setup/action.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Setup -description: Setup Node.js and install dependencies - -runs: - using: composite - steps: - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version-file: .nvmrc - - - name: Cache dependencies - id: yarn-cache - uses: actions/cache@v3 - with: - path: | - **/node_modules - .yarn/install-state.gz - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} - restore-keys: | - ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - ${{ runner.os }}-yarn- - - - name: Install dependencies - if: steps.yarn-cache.outputs.cache-hit != 'true' - run: yarn install --immutable - shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 6297ef1..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,154 +0,0 @@ -name: CI -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Lint files - run: yarn lint - - - name: Typecheck files - run: yarn typecheck - - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Run unit tests - run: yarn test --maxWorkers=2 --coverage - - build-library: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Build package - run: yarn prepare - - build-android: - runs-on: ubuntu-latest - env: - TURBO_CACHE_DIR: .turbo/android - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Cache turborepo for Android - uses: actions/cache@v3 - with: - path: ${{ env.TURBO_CACHE_DIR }} - key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turborepo-android- - - - name: Check turborepo cache for Android - run: | - TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status") - - if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then - echo "turbo_cache_hit=1" >> $GITHUB_ENV - fi - - - name: Install JDK - if: env.turbo_cache_hit != 1 - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '17' - - - name: Finalize Android SDK - if: env.turbo_cache_hit != 1 - run: | - /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null" - - - name: Cache Gradle - if: env.turbo_cache_hit != 1 - uses: actions/cache@v3 - with: - path: | - ~/.gradle/wrapper - ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Build example for Android - env: - JAVA_OPTS: "-XX:MaxHeapSize=6g" - run: | - yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" - - build-ios: - runs-on: macos-14 - env: - TURBO_CACHE_DIR: .turbo/ios - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - - - name: Cache turborepo for iOS - uses: actions/cache@v3 - with: - path: ${{ env.TURBO_CACHE_DIR }} - key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-turborepo-ios- - - - name: Check turborepo cache for iOS - run: | - TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status") - - if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then - echo "turbo_cache_hit=1" >> $GITHUB_ENV - fi - - - name: Cache cocoapods - if: env.turbo_cache_hit != 1 - id: cocoapods-cache - uses: actions/cache@v3 - with: - path: | - **/ios/Pods - key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }} - restore-keys: | - ${{ runner.os }}-cocoapods- - - - name: Install cocoapods - if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true' - run: | - cd example/ios - pod install - env: - NO_FLIPPER: 1 - - - name: Build example for iOS - run: | - yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4aab5ed --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,75 @@ +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: "Version bump type" + required: true + type: choice + options: + - patch + - minor + - major + +jobs: + release: + runs-on: ubuntu-latest + + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Enable corepack + run: corepack enable + + - name: Bump version and update CHANGELOG + id: bump + run: | + NEW_VERSION=$(npm version ${{ github.event.inputs.version }} --no-git-tag-version) + echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT + sed -i "s/## \[Unreleased\]/## [Unreleased]\n\n## [$NEW_VERSION] - $(date +%Y-%m-%d)/" CHANGELOG.md + + - name: Extract release notes + id: changelog + run: | + NOTES=$(awk '/## \[Unreleased\]/ {flag=1; next} /^## \[/ {flag=0} flag {print}' CHANGELOG.md) + echo "notes<> $GITHUB_OUTPUT + echo "$NOTES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Build and pack + id: pack + run: | + yarn install + yarn prepare + yarn pack --filename mendix-native-${{ steps.bump.outputs.version }}.tgz + echo "tgz=mendix-native-${{ steps.bump.outputs.version }}.tgz" >> $GITHUB_OUTPUT + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.bump.outputs.version }} + name: ${{ steps.bump.outputs.version }} + body: ${{ steps.changelog.outputs.notes }} + files: ${{ steps.pack.outputs.tgz }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6 + with: + commit-message: "chore: release ${{ steps.bump.outputs.version }}" + branch: release/${{ steps.bump.outputs.version }} + title: "Release ${{ steps.bump.outputs.version }}" + body: "This PR updates CHANGELOG.md and package.json for release ${{ steps.bump.outputs.version }}." + base: main diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml deleted file mode 100644 index 9509231..0000000 --- a/.github/workflows/secret-scan.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Secret Scan - -on: - push: - branches: [ "**" ] - pull_request: - branches: [ "**" ] - workflow_dispatch: - -permissions: - contents: read - security-events: write - -jobs: - gitleaks: - name: Gitleaks Scan - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Run gitleaks - id: gitleaks - uses: gitleaks/gitleaks-action@v2 - with: - config-path: .gitleaks.toml - args: --report-format sarif --report-path gitleaks.sarif - - - name: Upload SARIF to code scanning - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: gitleaks.sarif diff --git a/.gitignore b/.gitignore index 821dab1..d8bf499 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ node_modules/ npm-debug.log yarn-debug.log yarn-error.log +*.tgz # BUCK buck-out/ diff --git a/.gitleaks.toml b/.gitleaks.toml deleted file mode 100644 index 1faa223..0000000 --- a/.gitleaks.toml +++ /dev/null @@ -1,71 +0,0 @@ -title = "mendix-native gitleaks config" -# Base config uses gitleaks defaults; we extend with allowlists and a few custom regexes - -[allowlist] - description = "Global allowlist" - files = [ - "yarn.lock", - "package-lock.json", - "pnpm-lock.yaml", - "gradlew", - "gradlew.bat", - "example/ios/Pods/", - "example/android/" - ] - regexes = [ - # Common false positives - '''(?i)localhost(:[0-9]{2,5})?''', - '''(?i)internal-slot''', - '''(?i)eastasianwidth''' - ] - -[[rules]] - id = "generic-api-key" - description = "Generic API key format" - regex = '''(?i)(api|access|auth)[_-]?key["'\s:=]+[A-Za-z0-9_\-]{16,}''' - tags = ["api", "key", "generic"] - -[[rules]] - id = "bearer-token-inline" - description = "Potential hard-coded bearer token" - regex = '''Bearer\s+[A-Za-z0-9\-_.]{20,}''' - tags = ["auth", "token"] - -[[rules]] - id = "jwt" - description = "JSON Web Token" - regex = '''eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}''' - tags = ["jwt", "token"] - -[[rules]] - id = "aws-access-key" - description = "AWS Access Key ID" - regex = '''AKIA[0-9A-Z]{16}''' - tags = ["aws", "key"] - -[[rules]] - id = "github-token" - description = "GitHub Personal Access Token" - regex = '''ghp_[A-Za-z0-9]{36,}''' - tags = ["github", "token"] - -[[rules]] - id = "slack-token" - description = "Slack token" - regex = '''xox[baprs]-[A-Za-z0-9\-]{10,}''' - tags = ["slack", "token"] - -[[rules]] - id = "stripe-secret-key" - description = "Stripe live secret key" - regex = '''sk_live_[0-9a-zA-Z]{10,}''' - tags = ["stripe", "secret"] - -[[rules]] - id = "private-key-block" - description = "Private key block" - regex = '''-----BEGIN (EC|RSA|DSA|OPENSSH|PRIVATE) KEY-----''' - tags = ["crypto", "private-key"] - -[whitelist] # backward compatibility for older gitleaks versions - description = "Legacy whitelist alias" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..593a8ce --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to `mendix-native` package will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +## [Unreleased] + +## [0.1.2] - 2025-11-17 + +- We upgraded mendix-native to use React Native’s New Architecture to improve performance and future compatibility. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 62fbd6b..31e6e73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,7 +52,7 @@ yarn example ios To confirm that the app is running with the new architecture, you can check the Metro logs for a message like this: ```sh -Running "MendixNativeExample" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1} +Running "App" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1} ``` Note the `"fabric":true` and `"concurrentRoot":true` properties. @@ -97,16 +97,6 @@ We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint] Our pre-commit hooks verify that the linter and tests pass when committing. -### Publishing to npm - -We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc. - -To publish new versions, run the following: - -```sh -yarn release -``` - ### Scripts The `package.json` file contains various scripts for common tasks: diff --git a/README.md b/README.md index e69de29..2a8be0b 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,260 @@ +# Mendix Native + +Mendix native mobile package for React Native applications. + +## Prerequisites + +Before you begin, ensure you have the following installed: + +- **Node.js**: Version 18 (specified in `.nvmrc`) +- **Yarn**: Package manager (Yarn workspaces are required) +- **React Native development environment** (optional, only needed if running the example app): Follow the [React Native environment setup guide](https://reactnative.dev/docs/environment-setup) + - For iOS: Xcode and CocoaPods + - For Android: Android Studio and Android SDK + +## Local Development Setup + +This project is a monorepo managed using Yarn workspaces with: +- The library package in the root directory +- An example app in the `example/` directory + +### 1. Clone the Repository + +```bash +git clone https://github.com/YogendraShelke/mendix-native.git +cd mendix-native +``` + +### 2. Enable Corepack and Install Dependencies + +```bash +corepack enable +yarn install +``` + +This will enable Yarn via Corepack and install dependencies for both the library and the example app. + +### 3. Build the Library + +```bash +yarn prepare +``` + +This compiles the TypeScript code and generates the `lib` folder. + +### 4. Run the Example App + +The example app demonstrates library usage and reflects your local changes in real-time. + +#### Start Metro Bundler + +```bash +yarn example start +``` + +#### Run on iOS + +```bash +yarn example ios +``` + +Or open in Xcode: + +```bash +yarn dev:ios +``` + +#### Run on Android + +```bash +yarn example android +``` + +### 5. Development Workflow + +#### Making Changes + +- **JavaScript/TypeScript changes**: Automatically reflected without rebuild +- **Native code changes**: Require rebuilding the example app + +#### Edit Native Code + +**iOS (Objective-C/Swift):** +- Open `example/ios/MendixNativeExample.xcworkspace` in Xcode +- Find source files at: `Pods > Development Pods > mendix-native` + +**Android (Java/Kotlin):** +- Open `example/android` in Android Studio +- Find source files under: `mendix-native` in the Android view + +#### Verify Your Code + +Run type checking: + +```bash +yarn typecheck +``` + +Run linter: + +```bash +yarn lint +``` + +Fix linting issues: + +```bash +yarn lint --fix +``` + +Run tests: + +```bash +yarn test +``` + +### 6. Clean Build Artifacts + +If you encounter build issues: + +```bash +yarn clean +``` + +## Available Scripts + +- `yarn install` - Install dependencies +- `yarn prepare` - Build the library +- `yarn typecheck` - Type-check with TypeScript +- `yarn lint` - Lint code with ESLint +- `yarn test` - Run unit tests +- `yarn clean` - Remove build artifacts +- `yarn example start` - Start Metro bundler +- `yarn example ios` - Run example app on iOS +- `yarn example android` - Run example app on Android +- `yarn dev:ios` - Build, install pods, and open iOS project + +## Contributing + +See [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed contribution guidelines. + +--- + +## Release Process + +This repository supports automated releases using a GitHub Actions workflow. +A manual fallback process is also documented below for situations where the workflow cannot be used. + +## Automated Release (Recommended) + +The automated workflow handles: + +- Bumping the package version +- Building and generating the lib folder +- Creating the tarball (.tgz) +- Creating a GitHub Release with attached artifact +- Moving the Unreleased notes from CHANGELOG.md into a new version section +- Creating a pull request against master with updated files + +### Steps + +1. Go to GitHub → Actions → Manual Release. +2. Select Run workflow. +3. Choose the version bump type: + - patch + - minor + - major +4. Run the workflow. + +The workflow will: + +- Update package.json version +- Run `yarn install && yarn prepare` to generate the build +- Run `yarn pack` to generate the .tgz file +- Create a new GitHub Release with: + - tag = new version + - release notes from the Unreleased section in CHANGELOG.md + - the generated .tgz file attached +- Commit updated CHANGELOG.md and package.json +- Create a pull request: + - branch: `release/` + - base: `master` + +After the PR is merged, the release is complete. + +## Manual Release (Fallback) + +If the automated workflow fails, the release can be performed manually with the same steps. + +### 1. Bump the Version + +Choose one: + +```bash +npm version patch +npm version minor +npm version major +``` + +This updates package.json locally (you'll commit this later). + +### 2. Install and Build + +```bash +yarn install +yarn prepare +``` + +The prepare script should generate the lib folder. + +### 3. Generate the Tarball + +```bash +yarn pack +``` + +This creates a file like: + +``` +package-name-vX.Y.Z.tgz +``` + +Keep this file for the release. + +### 4. Update CHANGELOG.md + +Move the content under Unreleased into a new version heading. + +Example: + +```markdown +## [Unreleased] + +## [1.2.0] - 2025-01-15 + +``` + +### 5. Commit Changes + +```bash +git add CHANGELOG.md package.json +git commit -m "chore: release " +git push +``` + +### 6. Create a GitHub Release Manually + +1. Go to Releases → Draft a new release. +2. Use the version number as the tag and title. +3. Paste the notes you moved from the Unreleased section. +4. Upload the .tgz file from the yarn pack step. +5. Publish the release. + +### 7. Create Pull Request (If Needed) + +If your project requires PR-based updates, create a PR against master with the same changes you committed. + +## Summary + +Use the automated workflow whenever possible because it handles every repetitive step. +When needed, the fallback manual process mirrors the workflow exactly so releases remain consistent and predictable. From 559e051f89cc8c21d1ffcb68adb1b99b246f9e50 Mon Sep 17 00:00:00 2001 From: Yogendra Shelke <25844542+YogendraShelke@users.noreply.github.com> Date: Wed, 3 Dec 2025 21:37:58 +0530 Subject: [PATCH 2/4] chore: address review comments --- .github/workflows/release.yml | 10 +++++----- .nvmrc | 2 +- lefthook.yml | 8 ++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4aab5ed..27a474c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,14 +22,14 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd with: fetch-depth: 0 - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 with: - node-version: 20 + node-version-file: '.nvmrc' - name: Enable corepack run: corepack enable @@ -58,7 +58,7 @@ jobs: echo "tgz=mendix-native-${{ steps.bump.outputs.version }}.tgz" >> $GITHUB_OUTPUT - name: Create GitHub Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b with: tag_name: ${{ steps.bump.outputs.version }} name: ${{ steps.bump.outputs.version }} @@ -66,7 +66,7 @@ jobs: files: ${{ steps.pack.outputs.tgz }} - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 with: commit-message: "chore: release ${{ steps.bump.outputs.version }}" branch: release/${{ steps.bump.outputs.version }} diff --git a/.nvmrc b/.nvmrc index 3f430af..a45fd52 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18 +24 diff --git a/lefthook.yml b/lefthook.yml index 9695c12..fd933c8 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -7,6 +7,14 @@ pre-commit: types: glob: "*.{js,ts, jsx, tsx}" run: npx tsc + version-check: + glob: "package.json" + run: | + if git diff --cached package.json | grep -q '^\+.*"version":'; then + echo "❌ Error: Direct version bumps in package.json are not allowed." + echo "Please use the release workflow to bump versions." + exit 1 + fi commit-msg: parallel: true commands: From 3080533fe39f16d943737269ff29913e96439420 Mon Sep 17 00:00:00 2001 From: Yogendra Shelke <25844542+YogendraShelke@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:56:25 +0530 Subject: [PATCH 3/4] chore: add ci job --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0c2d2bf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +on: + pull_request: + +jobs: + lint-and-build: + name: Lint, Type Check & Build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 + with: + node-version-file: '.nvmrc' + + - name: Enable corepack + run: corepack enable + + - name: Install dependencies + run: yarn install --immutable + + - name: Type check + run: yarn typecheck + + - name: Lint check + run: yarn lint + + - name: Build + run: yarn prepare From 425f2b3ecf6a023f36c46859f29bf722de5fb7c7 Mon Sep 17 00:00:00 2001 From: Yogendra Shelke <25844542+YogendraShelke@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:02:15 +0530 Subject: [PATCH 4/4] chore: update error message --- lefthook.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lefthook.yml b/lefthook.yml index fd933c8..b5ccec8 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -11,8 +11,7 @@ pre-commit: glob: "package.json" run: | if git diff --cached package.json | grep -q '^\+.*"version":'; then - echo "❌ Error: Direct version bumps in package.json are not allowed." - echo "Please use the release workflow to bump versions." + echo "❌ Please don't edit the version manually. Version bumps are handled automatically when the package is released via the release workflow." exit 1 fi commit-msg: