From 246413550f56207e179a6dc36dc4a34acb2ae1fa Mon Sep 17 00:00:00 2001 From: Plastikfan <49785914+plastikfan@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:34:33 +0000 Subject: [PATCH] Initial commit --- .gitattributes | 1 + .github/dependabot.yml | 6 + .github/workflows/auto-check-workflow.yml | 64 +++ .github/workflows/ci-workflow.yml | 75 +++ .github/workflows/release-workflow.yml | 35 ++ .gitignore | 30 + .golangci.yml | 72 +++ .goreleaser.yaml | 51 ++ .pre-commit-config.yaml | 11 + .vscode/launch.json | 15 + .vscode/settings.json | 65 +++ LICENSE | 21 + README.md | 160 ++++++ Taskfile.yml | 199 +++++++ VERSION | 1 + go.mod | 26 + go.sum | 52 ++ internal/lab/test-utilities.go | 60 ++ locale/default/astrolib.active.en-GB.json | 22 + locale/deploy/astrolib.active.en-US.json | 27 + locale/locale-suite_test.go | 13 + locale/messages-errors.go | 51 ++ locale/messages-general.go | 18 + locale/out/active.en-GB.json | 22 + locale/out/active.en-US.json | 1 + .../out/en-US/astrolib.translate.en-US.json | 0 locale/out/translate.en-US.json | 27 + locale/test-i18n-messages_test.go | 50 ++ locale/translate-defs.go | 11 + resources/doc/i18n-README.md | 72 +++ resources/images/go-logo-light-blue.png | Bin 0 -> 15569 bytes scripts/apply-coverage-exclusions.sh | 34 ++ scripts/automate-checklist.sh | 243 ++++++++ scripts/coverage-exclusion-list.txt | 1 + scripts/git-workflow.gum.sh | 537 ++++++++++++++++++ scripts/git-workflow.zshrc | 221 +++++++ .../data/l10n/test.astrolib.active.en-US.json | 7 + .../data/l10n/test.graffico.active.en-US.json | 7 + 38 files changed, 2308 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/auto-check-workflow.yml create mode 100644 .github/workflows/ci-workflow.yml create mode 100644 .github/workflows/release-workflow.yml create mode 100644 .gitignore create mode 100644 .golangci.yml create mode 100644 .goreleaser.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Taskfile.yml create mode 100644 VERSION create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/lab/test-utilities.go create mode 100644 locale/default/astrolib.active.en-GB.json create mode 100644 locale/deploy/astrolib.active.en-US.json create mode 100644 locale/locale-suite_test.go create mode 100644 locale/messages-errors.go create mode 100644 locale/messages-general.go create mode 100644 locale/out/active.en-GB.json create mode 100644 locale/out/active.en-US.json create mode 100644 locale/out/en-US/astrolib.translate.en-US.json create mode 100644 locale/out/translate.en-US.json create mode 100644 locale/test-i18n-messages_test.go create mode 100644 locale/translate-defs.go create mode 100644 resources/doc/i18n-README.md create mode 100644 resources/images/go-logo-light-blue.png create mode 100755 scripts/apply-coverage-exclusions.sh create mode 100644 scripts/automate-checklist.sh create mode 100644 scripts/coverage-exclusion-list.txt create mode 100644 scripts/git-workflow.gum.sh create mode 100644 scripts/git-workflow.zshrc create mode 100644 test/data/l10n/test.astrolib.active.en-US.json create mode 100644 test/data/l10n/test.graffico.active.en-US.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d207b18 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.go text eol=lf diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..9aa5f33 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: gomod + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/auto-check-workflow.yml b/.github/workflows/auto-check-workflow.yml new file mode 100644 index 0000000..d4659e0 --- /dev/null +++ b/.github/workflows/auto-check-workflow.yml @@ -0,0 +1,64 @@ +name: Initialise Repo +on: + create: + branches: + - main + +permissions: + contents: write + +jobs: + run-script: + name: run auto-check script + + # We will only run this action when this repository isn't the + # template repository + # + if: >- + ${{ !github.event.repository.is_template }} + + runs-on: ubuntu-latest + + steps: + - name: Configure Git + run: | + git config --global user.name "${GITHUB_ACTOR}" + git config --global user.email "${GITHUB_ACTOR_EMAIL}" + shell: bash + + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.ref }} + + - name: Run Auto Check script + run: | + . ./scripts/automate-checklist.sh + auto-check + shell: bash + + - name: Go Format + run: | + go fmt ./... + shell: bash + + - name: Clean up redundant scripts + run: | + rm -fv .github/workflows/auto-check-workflow.yml 2>/dev/null + rm -fv ./scripts/automate-checklist.sh 2>/dev/null + shell: bash + + - name: Add files and commit changes + run: | + echo "* available branches:" + git branch | cat + echo "default branch: '${{ github.event.repository.default_branch }}'" + echo "========" + git fetch + git checkout main + git add . + git commit -m "chore(gh-actions): apply auto-check edits" + git push -u origin ${{ github.event.repository.default_branch }} + shell: bash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml new file mode 100644 index 0000000..62e1bd9 --- /dev/null +++ b/.github/workflows/ci-workflow.yml @@ -0,0 +1,75 @@ +name: Astrolib Continuous Integration + +on: + push: + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v4 + with: + go-version: 1.23 + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.60.3 + args: --verbose + + test: + strategy: + matrix: + go-version: [1.23] + platform: [ubuntu-latest, macos-latest] + + runs-on: ${{ matrix.platform }} + + env: + COVER_DIR: ${{ github.workspace }}/coverage + COVER_FILE: coverage.out + COVER_OUT_PATH: ${{ github.workspace }}/coverage/coverage.out + COVER_HTML_PATH: ${{ github.workspace }}/coverage/coverage.html + GINKGO_REPORT: ginkgo.report + + steps: + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go-version }} + + - name: Install goveralls + run: go install github.com/mattn/goveralls@latest + + - name: Install ginkgo + run: go install github.com/onsi/ginkgo/v2/ginkgo@v2.20.2 + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Ensure coverage directory exists + run: | + mkdir -p ${{ github.workspace }}/coverage + + - name: Run tests and generate coverage profile with Ginkgo + run: | + ginkgo run -r -json-report {{env.GINKGO_REPORT}} -coverpkg=./... -coverprofile=coverage.out + + - name: Apply coverage exclusions + run: | + ${{ github.workspace }}/scripts/apply-coverage-exclusions.sh + + - name: Check coverage directory contents + run: | + echo "Contents of ${{ github.workspace }}/coverage:" + ls -la ${{ github.workspace }}/coverage + + - name: Generate HTML coverage report + run: | + go tool cover -html=coverage.out -o ${{ github.workspace }}/coverage/coverage.html + + - name: Upload coverage to Coveralls + uses: shogo82148/actions-goveralls@v1 + with: + path-to-profile: coverage.out diff --git a/.github/workflows/release-workflow.yml b/.github/workflows/release-workflow.yml new file mode 100644 index 0000000..22ec830 --- /dev/null +++ b/.github/workflows/release-workflow.yml @@ -0,0 +1,35 @@ +name: Astrolib Release + +on: + push: + tags: + - '*' + +permissions: + contents: write + +jobs: + goreleaser: + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Git Fetch + run: git fetch --force --tags + - name: Setup + uses: actions/setup-go@v3 + with: + go-version: '>=1.23' + cache: true + - name: Generate Changelog Only + uses: goreleaser/goreleaser-action@v4 + with: + distribution: goreleaser + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..805ecb2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +*.env + +# Dependency directories (remove the comment below to include it) +# vendor/ + +coverage +coverage.out +ginkgo.report +report.json +coverage.html + +.task/ + +i18n/out/en-US/active.en-GB.json + +.DS_Store +thumbs.db +.tmp diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..7ac922c --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,72 @@ +linters-settings: + errcheck: + check-type-assertions: true + goconst: + min-len: 2 + min-occurrences: 3 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + govet: + shadow: true + disable: + - fieldalignment # too strict + + nolintlint: + require-explanation: false + require-specific: true +linters: + disable-all: true + enable: + - bodyclose + # - deadcode + # depguard needs to be reviewed properly and then configured, before + # it can be re-enabled. + # https://github.com/OpenPeeDeeP/depguard#example-configs + # - depguard + - copyloopvar + - dogsled + # - dupl + - errcheck + - exhaustive + - goconst + - gocritic + - gofmt + - goimports + - gocyclo + - gosec + - gosimple + - govet + - ineffassign + - misspell + - mnd + - nolintlint + - nakedret + - prealloc + - predeclared + - revive + - staticcheck + # - structcheck + - stylecheck + - thelper + - tparallel + - unconvert + - unparam + # - varcheck + - whitespace + - wsl + +issues: + max-issues-per-linter: 0 + max-same-issues: 0 + fix: true + exclude: + - "cuddle" + +run: + issues-exit-code: 1 + timeout: "5m" diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..6edce4e --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,51 @@ +before: + hooks: + - go mod tidy + # ? do we need a - go mod generate + # - go mod generate + +builds: + - id: "astrolib" + env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + no_main_check: true + skip: true + +archives: + - format: tar.gz + # this name template makes the OS and Arch compatible with the results of uname. + name_template: >- + {{ .ProjectName }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + {{- if .Arm }}v{{ .Arm }}{{ end }} + # use zip for windows archives + format_overrides: + - goos: windows + format: zip +checksum: + name_template: "checksums.txt" +snapshot: + name_template: "{{ incpatch .Version }}-next" +changelog: + use: github + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + groups: + - title: "πŸš€ Features" + regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$' + order: 0 + - title: "πŸ› Bug fixes" + regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$' + order: 1 + - title: "πŸ₯ Others" + order: 999 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..0d8ce6d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,11 @@ +repos: + - repo: https://github.com/golangci/golangci-lint + rev: v1.52.2 + hooks: + - id: golangci-lint + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: no-commit-to-branch + args: ["--branch", "master", "--branch", "main"] diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0d1185b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9cd417a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,65 @@ +{ + "go.lintTool": "golangci-lint", + "go.lintFlags": [ + "--fast" + ], + "cSpell.words": [ + "astrolib", + "bodyclose", + "cmds", + "copyloopvar", + "coverpkg", + "coverprofile", + "cubiest", + "deadcode", + "depguard", + "dogsled", + "dotenv", + "dupl", + "errcheck", + "exportloopref", + "fieldalignment", + "goconst", + "gocritic", + "gocyclo", + "gofmt", + "goimports", + "golangci", + "gomega", + "gomnd", + "goreleaser", + "gosec", + "gosimple", + "goveralls", + "govet", + "graffico", + "ineffassign", + "jibberjabber", + "linters", + "mattn", + "nakedret", + "nicksnyder", + "nolint", + "nolintlint", + "onsi", + "outdir", + "pixa", + "prealloc", + "repotoken", + "shogo", + "sidewalk", + "snivilised", + "staticcheck", + "structcheck", + "stylecheck", + "thelper", + "tparallel", + "typecheck", + "unconvert", + "unparam", + "varcheck", + "watchv", + "watchvc", + "watchvi" + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bdfac1c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 snivilised + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b66ad24 --- /dev/null +++ b/README.md @@ -0,0 +1,160 @@ +# 🌟 astrolib: ___Go template for library modules___ + +[![A B](https://img.shields.io/badge/branching-commonflow-informational?style=flat)](https://commonflow.org) +[![A B](https://img.shields.io/badge/merge-rebase-informational?style=flat)](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) +[![A B](https://img.shields.io/badge/branch%20history-linear-blue?style=flat)](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule) +[![Go Reference](https://pkg.go.dev/badge/github.com/snivilised/astrolib.svg)](https://pkg.go.dev/github.com/snivilised/astrolib) +[![Go report](https://goreportcard.com/badge/github.com/snivilised/astrolib)](https://goreportcard.com/report/github.com/snivilised/astrolib) +[![Coverage Status](https://coveralls.io/repos/github/snivilised/astrolib/badge.svg?branch=main)](https://coveralls.io/github/snivilised/astrolib?branch=main&kill_cache=1) +[![Astrolib Continuous Integration](https://github.com/snivilised/astrolib/actions/workflows/ci-workflow.yml/badge.svg)](https://github.com/snivilised/astrolib/actions/workflows/ci-workflow.yml) +[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) +[![A B](https://img.shields.io/badge/commit-conventional-commits?style=flat)](https://www.conventionalcommits.org/) + + + + + + + + + + + + + + + + +

+ +

+ +## πŸ”° Introduction + +This project is a template to aid in the startup of Go library module projects. + +## πŸ“š Usage + +## πŸŽ€ Features + +

+ + +

+ ++ unit testing with [Ginkgo](https://onsi.github.io/ginkgo/)/[Gomega](https://onsi.github.io/gomega/) ++ implemented with [🐍 Cobra](https://cobra.dev/) cli framework, assisted by [🐲 Cobrass](https://github.com/snivilised/cobrass) ++ i18n with [go-i18n](https://github.com/nicksnyder/go-i18n) ++ linting configuration and pre-commit hooks, (see: [linting-golang](https://freshman.tech/linting-golang/)). + +## πŸ”¨ Developer Info + +By using this template, there is no need to use the cobra-cli to scaffold your application as this has been done already. It should be noted that the structure that is generated by the cobra-cli has been significantly changed in this template, mainly to remove use of the __init()__ function and to minimise use of package level global variables. For a rationale, see [go-without-package-scoped-variables](https://dave.cheney.net/2017/06/11/go-without-package-scoped-variables). + +### πŸ“ Checklist of required changes + +The following is list of actions that must be performed before using this template. Most of the changes concern changing the name `astrolib` to the name of the new application. As the template is instantiated from github, the new name will automatically replace the top level directory name, that being ___astrolib___. + +βž• The following descriptions use owner name ___pandora___ and repo name ___maestro___ as an example. That is to say the client has instantiated ___astrolib___ template into github at url _github.com/pandora/maestro_ + +#### πŸ€– Automated changes + +Automated via `automate-checklist.sh` script. When the user instantiates the repo, a github actions workflow is executed which applies changes to the clients repo automatically. The following description describes the changes that are applied on the user's behalf and the workflow is automatically deleted. However, there are other changes that should be made. These compose the manual checklist and should be heeded by the user. + +##### βœ… Rename import statements + ++ `rename import paths`: global search and replace ___snivilised/astrolib___ to ___pandora/maestro___ + +##### βœ… Identifiers + ++ `change astrolibTemplData`: perform a refactor rename (_Rename Symbol_) to ___maestroTemplData___ + +##### βœ… Global search replace astrolib to maestro + +Will take care of the following required changes: + ++ `change module name`: update the module name inside the .mod file in the root directory ++ `change ApplicationName`: modify to reflect the new application name. This application name is incorporated into the name of any translation files to be loaded. ++ `update BINARY_NAME`: Inside _Taskfile.yml_, change the value of ___BINARY_NAME___ to the name of the client application. ++ `update github action workflows`: change the name of the workflows in the .yaml files to replace ___astrolib___ to ___Maestro___ (note the change of case, if this is important). + +##### βœ… Localisation/Internationalisation + ++ `change the names of the translation files`: eg change ___astrolib.active.en-GB.json___ to ___maestro.active.en-GB.json___ + +##### βœ… Miscellaneous automated changes + ++ `reset version files`: this is optional because the release process automatically updates the version number according to the tag specified by the user, but will initially contain the version number which reflects the current value of astrolib at the time the client project is instantiated. ++ `change SOURCE_ID`: to "github.com/pandora/maestro" + +#### πŸ– Manual changes + +The following documents manual changes required. Manual checklist: + +##### β˜‘οΈ Structural changes + ++ `github actions workflow`: If the client does not to use github actions workflow automation, then these files ([ci-workflow](.github/workflows/ci-workflow.yml), [release-workflow](.github/workflows/release-workflow.yml), [.goreleaser.yaml](./.goreleaser.yaml)), should be deleted. + ++ `rename the widget command`: rename __widget-cmd.go__ and its associated test __widget_test.go__ to whatever is the first command to be implemented in the application. The widget command can serve as a template as to how to define a new command, without having to start from scratch. It will be easier for the user to modify an existing command, so just perform a case sensitive search and replace for ___widget/Widget___ and replace with ___Foo/foo___ where foo represents the new command to be created. + ++ `review bootstrap.go`: this will need to be modified to invoke creation of any custom commands. The `execute` method of __bootstrap__ should be modified to invoke command builder. Refer to the `widget` command to see how this is done. + +#### β˜‘οΈ Github changes + +Unfortunately, github doesn't copy over the template project's settings to the client project, so these changes must be made manually: + +Under `Protect matching branches` + ++ `Require a pull request before merging` βœ… _ENABLE_ ++ `Require linear history` βœ… _ENABLE_ ++ `Do not allow bypassing the above settings` βœ… _ENABLE_ + +Of course, its up to the user what settings they use in their repo, these are just recommended as a matter of good practice. + +#### β˜‘οΈ Code coverage + ++ `coveralls.io`: add maestro project + +#### β˜‘οΈ Miscellaneous changes + ++ `replace README content` ++ `update email address in copyright statement`: The __root.go__ file contains a placeholder for an email address, update this comment accordingly. ++ `create .env file`: Add any appropriate secrets to a newly created .env in the root directory and to enable the __deploy__ task to work, define a __DEPLOY_TO__ entry that defines where builds should be deployed to for testing ++ `install pre-commit hooks`: just run ___pre-commit install___ ++ `update translation file`: Inside _Taskfile.yml_, add support for loading any translations that the app will support. By default, it deploys a translation file for __en-US__ so this needs to be updated as appropriate. + +### 🌐 l10n Translations + +This template has been setup to support localisation. The default language is `en-GB` with support for `en-US`. There is a translation file for `en-US` defined as __src/i18n/deploy/astrolib.active.en-US.json__. This is the initial translation for `en-US` that should be deployed with the app. + +Make sure that the go-i18n package has been installed so that it can be invoked as cli, see [go-i18n](https://github.com/nicksnyder/go-i18n) for installation instructions. + +To maintain localisation of the application, the user must take care to implement all steps to ensure translate-ability of all user facing messages. Whenever there is a need to add/change user facing messages including error messages, to maintain this state, the user must: + ++ define template struct (__xxxTemplData__) in __src/i18n/messages.go__ and corresponding __Message()__ method. All messages are defined here in the same location, simplifying the message extraction process as all extractable strings occur at the same place. Please see [go-i18n](https://github.com/nicksnyder/go-i18n) for all translation/pluralisation options and other regional sensitive content. + +For more detailed workflow instructions relating to i18n, please see [i18n README](./resources/doc/i18n-README.md) + +### πŸ§ͺ Quick Test + +To check the app is working (as opposed to running the unit tests), build and deploy: + +> task tbd + +(which performs a test, build then deploy) + +NB: the `deploy` task has been set up for windows by default, but can be changed at will. + +Check that the executable and the US language file __maestro.active.en-US.json__ have both been deployed. Then invoke the widget command with something like + +> maestro widget -p "P?\" -t 30 + +Optionally, the user can also specify the ___directory___ flag: + +> maestro widget -p "P?\" -t 30 -d foo-bar.txt + +... where ___foo-bar.txt___ should be replaced with a file that actually exists. + +This assumes that the the project name is `maestro`, change as appropriate. + +Since the `widget` command uses `Cobrass` option validation to check that the file specified exists, the app will fail if the file does not exist. This serves as an example of how to implement option validation with `Cobrass`. diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..33834c1 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,199 @@ +--- +# dependencies: +# - ginkgo +# - goi18n +# - golangci-lint +# - goveralls +# - yamllint + +version: "3" +silent: true + +dotenv: [".env"] + +vars: + FORMAT: json + BINARY_NAME: traverse + DEPLOY_DIR: ./locale/deploy + OUT_DIR: ./locale/out + L10N_DIR: ./locale/out/l10n + # + SOURCE_LANG: en-GB + SOURCE_ACTIVE: "active.{{.SOURCE_LANG}}.{{.FORMAT}}" + # + LANGUAGE_US: en-US + US_OUT_DIR: "{{.OUT_DIR}}/{{.LANGUAGE_US}}" + ACTIVE_US: "{{.BINARY_NAME}}.active.en-US.{{.FORMAT}}" + TRANSLATE_US: "{{.BINARY_NAME}}.translate.en-US.{{.FORMAT}}" + TRANSLATE_US_FILEPATH: "{{.US_OUT_DIR}}/{{.TRANSLATE_US}}" + COVER_DIR: "./" + COVER_FILE: "coverage.out" + COVER_HTML_PATH: "./coverage.html" + GINKGO_REPORT: "ginkgo.report" + +tasks: + # === build ================================================ + + b: + cmds: + - go build ./... + + # === test ================================================= + + t: + cmds: + - go test ./... + + ti: + cmds: + - go test ./i18n + + dry: + cmds: + - ginkgo -v --dry-run ./... + + # === ginkgo ================================================ + + # initialise a test suite for a package. (only 1 per package) + boot: + cmds: + - ginkgo bootstrap + + # run tests suites recursive + g: + cmds: + - ginkgo -r + + # invoke as task gen -- + gl: + cmds: + - ginkgo -r --label-filter={{.CLI_ARGS}} + + # run tests suites recursive with verbose + gv: + cmds: + - ginkgo -r -v + + # generate a test file for the item provided (item_test.go) + # invoke as task gen -- + gen: + cmds: + - ginkgo generate {{.CLI_ARGS}} + + # === watch ================================================ + + watchv: + cmds: + - ginkgo watch -v -r -p ./... + + watchvc: + cmds: + - ginkgo watch -v -r -p ./collections + + watchvi: + cmds: + - ginkgo watch -v -r -p ./i18n + + watch: + cmds: + - ginkgo watch -r -p ./... + + # === lint ================================================= + + lint: + cmds: + - golangci-lint run + + linty: + cmds: + - yamllint *.y*ml + + # === coverage ============================================= + + cover-clean: + cmds: + - rm -rf ./coverage + + cover-publish: + cmds: + - goveralls -repotoken {{.COVERALLS_TOKEN}} + + cover-setup: + cmds: + - mkdir -p ./coverage + + cover-ginkgo: + cmds: + - ginkgo run -r -json-report {{.GINKGO_REPORT}} -coverpkg=./... -coverprofile={{.COVER_FILE}} --output-dir {{.COVER_DIR}} + + cover-show: + cmds: + - open {{.COVER_HTML_PATH}} + + cover-exclude: + cmds: + - ./scripts/apply-coverage-exclusions.sh + + cover: + cmds: + - task: cover-setup + - task: cover-ginkgo + - task: cover-exclude + - go tool cover -html=./coverage.out -o {{.COVER_HTML_PATH}} + - open {{.COVER_HTML_PATH}} + + # === i18n ================================================= + + clear: + cmds: + - rm ./i18n/out/* --recursive + + # extract i18m messages + extract: + cmds: + - goi18n extract + -format json + -sourceLanguage "en-GB" + -outdir ./i18n/out/l10n + + # new translation + newt: + deps: [extract] + cmds: + - touch ./i18n/out/l10n/translate.en-US.json + + # derive a translation from the default + merge: + cmds: + - goi18n merge + -format json + -sourceLanguage "en-GB" + -outdir ./i18n/out + ./i18n/out/active.en-GB.json ./i18n/out/l10n/translate.en-US.json + + # update existing translations + # after running this task, the translation file generated will + # contain only the new translations. Update the active file, + # with the new translations. Also, need to copy the default + # file (active.en-GB.json) back into ./i18n/default + update: + deps: [extract] + cmds: + - goi18n merge + -format json + -sourceLanguage "en-GB" + -outdir ./i18n/out + ./i18n/out/active.en-GB.json ./i18n/deploy/active.en-US.json + + # run this after manual translation has occurred to integrate it + # back into the translation file. Unfortunately, this task doesn't + # work properly, because it does not include the hashes. Without + # this task, the new translations must be manually added to the active + # translation file (active.en-US.json). + accept: + cmds: + - goi18n merge + -format json + -sourceLanguage "en-US" + -outdir ./i18n/temp + ./i18n/out/translate.en-US.json ./i18n/deploy/active.en-US.json diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..f54b243 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +v0.3.4 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b924590 --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module github.com/snivilised/astrolib + +go 1.23.0 + +require ( + github.com/nicksnyder/go-i18n/v2 v2.4.1 + github.com/onsi/ginkgo/v2 v2.22.0 + github.com/onsi/gomega v1.36.0 + github.com/snivilised/li18ngo v0.1.9 +) + +require ( + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/snivilised/nefilim v0.1.10 // indirect + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect + golang.org/x/tools v0.27.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6927d48 --- /dev/null +++ b/go.sum @@ -0,0 +1,52 @@ +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs= +github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/nicksnyder/go-i18n/v2 v2.4.1 h1:zwzjtX4uYyiaU02K5Ia3zSkpJZrByARkRB4V3YPrr0g= +github.com/nicksnyder/go-i18n/v2 v2.4.1/go.mod h1:++Pl70FR6Cki7hdzZRnEEqdc2dJt+SAGotyFg/SvZMk= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.0 h1:Pb12RlruUtj4XUuPUqeEWc6j5DkVVVA49Uf6YLfC95Y= +github.com/onsi/gomega v1.36.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/snivilised/li18ngo v0.1.9 h1:xq+9yUv9JBY3fwzCzqCt0bIUdFlzDXcSjC2fQiSF3O0= +github.com/snivilised/li18ngo v0.1.9/go.mod h1:7soFZVy6K6P8jgHYn0fN2Sw0r66isPa/K/K+r3fl19Q= +github.com/snivilised/nefilim v0.1.10 h1:j8EGMZSGgFIjcxaVOvYvn7/r66Q5ffXihmq52F55r/E= +github.com/snivilised/nefilim v0.1.10/go.mod h1:xcQnPeB+zVD+xgw9yzy0QiMTGqjYNEWKeoaZL0TBQeA= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= +golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/lab/test-utilities.go b/internal/lab/test-utilities.go new file mode 100644 index 0000000..52fa034 --- /dev/null +++ b/internal/lab/test-utilities.go @@ -0,0 +1,60 @@ +package lab + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" +) + +func Path(parent, relative string) string { + segments := strings.Split(relative, "/") + return filepath.Join(append([]string{parent}, segments...)...) +} + +func Normalise(p string) string { + return strings.ReplaceAll(p, "/", string(filepath.Separator)) +} + +func Reason(name string) string { + return fmt.Sprintf("❌ for item named: '%v'", name) +} + +func JoinCwd(segments ...string) string { + if current, err := os.Getwd(); err == nil { + parent, _ := filepath.Split(current) + grand := filepath.Dir(parent) + great := filepath.Dir(grand) + all := append([]string{great}, segments...) + + return filepath.Join(all...) + } + + panic("could not get root path") +} + +func Root() string { + if current, err := os.Getwd(); err == nil { + return current + } + + panic("could not get root path") +} + +func Repo(relative string) string { + _, filename, _, _ := runtime.Caller(0) //nolint:dogsled // ignore + return Path(filepath.Dir(filename), relative) +} + +func Log() string { + if current, err := os.Getwd(); err == nil { + parent, _ := filepath.Split(current) + grand := filepath.Dir(parent) + great := filepath.Dir(grand) + + return filepath.Join(great, "Test", "test.log") + } + + panic("could not get root path") +} diff --git a/locale/default/astrolib.active.en-GB.json b/locale/default/astrolib.active.en-GB.json new file mode 100644 index 0000000..bb88f60 --- /dev/null +++ b/locale/default/astrolib.active.en-GB.json @@ -0,0 +1,22 @@ +{ + "root-command-config-file-usage": { + "description": "root command config flag usage", + "other": "config file (default is $HOME/{{.ConfigFileName}}.yml)" + }, + "root-command-language-usage": { + "description": "root command lang usage", + "other": "'lang' defines the language according to IETF BCP 47" + }, + "root-command-long-description": { + "description": "long description for the root command", + "other": "A longer description that spans multiple lines and likely contains\n\t\texamples and usage of using your application. For example:\n\t\t\n\t\tCobra is a CLI library for Go that empowers applications.\n\t\tThis application is a tool to generate the needed files\n\t\tto quickly create a Cobra application." + }, + "root-command-short-description": { + "description": "short description for the root command", + "other": "A brief description of your application" + }, + "using-config-file": { + "description": "Message to indicate which config is being used", + "other": "Using config file: {{.ConfigFileName}}" + } +} \ No newline at end of file diff --git a/locale/deploy/astrolib.active.en-US.json b/locale/deploy/astrolib.active.en-US.json new file mode 100644 index 0000000..1bdc419 --- /dev/null +++ b/locale/deploy/astrolib.active.en-US.json @@ -0,0 +1,27 @@ +{ + "root-command-config-file-usage": { + "description": "root command config flag usage", + "hash": "sha1-60ec091d99ab2286a4bb4a1e1733c155bca95a9f", + "other": "config file (default is $HOME/{{.ConfigFileName}}.yml)" + }, + "root-command-language-usage": { + "description": "root command lang usage", + "hash": "sha1-13eee706e037f33844c76f1dcdac5786b4089a00", + "other": "'lang' defines the language according to IETF BCP 47" + }, + "root-command-long-description": { + "description": "long description for the root command", + "hash": "sha1-9122dfb1f95d2d00501ca67449df1dcf695f2cc2", + "other": "A longer description that spans multiple lines and likely contains\n\t\texamples and usage of using your application. For example:\n\t\t\n\t\tCobra is a CLI library for Go that empowers applications.\n\t\tThis application is a tool to generate the needed files\n\t\tto quickly create a Cobra application." + }, + "root-command-short-description": { + "description": "short description for the root command", + "hash": "sha1-a88cc9526d5e47f670ce9b089390e65415ab26cc", + "other": "A brief description of your application" + }, + "using-config-file": { + "description": "Message to indicate which config is being used", + "hash": "sha1-06fd9528f8226d1501ea09bc5d629f4922b4f3c6", + "other": "Using config file: {{.ConfigFileName}}" + } +} \ No newline at end of file diff --git a/locale/locale-suite_test.go b/locale/locale-suite_test.go new file mode 100644 index 0000000..23aeb9d --- /dev/null +++ b/locale/locale-suite_test.go @@ -0,0 +1,13 @@ +package locale_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" //nolint:revive // ginkgo ok + . "github.com/onsi/gomega" //nolint:revive // gomega ok +) + +func TestLocale(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Locale Suite") +} diff --git a/locale/messages-errors.go b/locale/messages-errors.go new file mode 100644 index 0000000..861a6ce --- /dev/null +++ b/locale/messages-errors.go @@ -0,0 +1,51 @@ +package locale + +import ( + "github.com/nicksnyder/go-i18n/v2/i18n" + "github.com/snivilised/li18ngo" +) + +// ❌ FooBar + +// FooBarTemplData - TODO: this is a none existent error that should be +// replaced by the client. Its just defined here to illustrate the pattern +// that should be used to implement i18n with li18ngo. Also note, +// that this message has been removed from the translation files, so +// it is not useable at run time. +type FooBarTemplData struct { + astrolibTemplData + Path string + Reason error +} + +// the ID should use spp/library specific code, so replace astrolib with the +// name of the library implementing this template project. +func (td FooBarTemplData) Message() *i18n.Message { + return &i18n.Message{ + ID: "foo-bar.astrolib.nav", + Description: "Foo Bar description", + Other: "foo bar failure '{{.Path}}' (reason: {{.Reason}})", + } +} + +type FooBarError struct { + li18ngo.LocalisableError +} + +// FooBar enables the client to check if error is FooBarError +// via FooBarErrorBehaviourQuery +func (e FooBarError) FooBar() bool { + return true +} + +// NewFooBarError creates a FooBarError +func NewFooBarError(path string, reason error) FooBarError { + return FooBarError{ + LocalisableError: li18ngo.LocalisableError{ + Data: FooBarTemplData{ + Path: path, + Reason: reason, + }, + }, + } +} diff --git a/locale/messages-general.go b/locale/messages-general.go new file mode 100644 index 0000000..3614a5a --- /dev/null +++ b/locale/messages-general.go @@ -0,0 +1,18 @@ +package locale + +import ( + "github.com/nicksnyder/go-i18n/v2/i18n" +) + +type UsingConfigFileTemplData struct { + astrolibTemplData + ConfigFileName string +} + +func (td UsingConfigFileTemplData) Message() *i18n.Message { + return &i18n.Message{ + ID: "using-config-file", + Description: "Message to indicate which config is being used", + Other: "Using config file: {{.ConfigFileName}}", + } +} diff --git a/locale/out/active.en-GB.json b/locale/out/active.en-GB.json new file mode 100644 index 0000000..bb88f60 --- /dev/null +++ b/locale/out/active.en-GB.json @@ -0,0 +1,22 @@ +{ + "root-command-config-file-usage": { + "description": "root command config flag usage", + "other": "config file (default is $HOME/{{.ConfigFileName}}.yml)" + }, + "root-command-language-usage": { + "description": "root command lang usage", + "other": "'lang' defines the language according to IETF BCP 47" + }, + "root-command-long-description": { + "description": "long description for the root command", + "other": "A longer description that spans multiple lines and likely contains\n\t\texamples and usage of using your application. For example:\n\t\t\n\t\tCobra is a CLI library for Go that empowers applications.\n\t\tThis application is a tool to generate the needed files\n\t\tto quickly create a Cobra application." + }, + "root-command-short-description": { + "description": "short description for the root command", + "other": "A brief description of your application" + }, + "using-config-file": { + "description": "Message to indicate which config is being used", + "other": "Using config file: {{.ConfigFileName}}" + } +} \ No newline at end of file diff --git a/locale/out/active.en-US.json b/locale/out/active.en-US.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/locale/out/active.en-US.json @@ -0,0 +1 @@ +{} diff --git a/locale/out/en-US/astrolib.translate.en-US.json b/locale/out/en-US/astrolib.translate.en-US.json new file mode 100644 index 0000000..e69de29 diff --git a/locale/out/translate.en-US.json b/locale/out/translate.en-US.json new file mode 100644 index 0000000..1bdc419 --- /dev/null +++ b/locale/out/translate.en-US.json @@ -0,0 +1,27 @@ +{ + "root-command-config-file-usage": { + "description": "root command config flag usage", + "hash": "sha1-60ec091d99ab2286a4bb4a1e1733c155bca95a9f", + "other": "config file (default is $HOME/{{.ConfigFileName}}.yml)" + }, + "root-command-language-usage": { + "description": "root command lang usage", + "hash": "sha1-13eee706e037f33844c76f1dcdac5786b4089a00", + "other": "'lang' defines the language according to IETF BCP 47" + }, + "root-command-long-description": { + "description": "long description for the root command", + "hash": "sha1-9122dfb1f95d2d00501ca67449df1dcf695f2cc2", + "other": "A longer description that spans multiple lines and likely contains\n\t\texamples and usage of using your application. For example:\n\t\t\n\t\tCobra is a CLI library for Go that empowers applications.\n\t\tThis application is a tool to generate the needed files\n\t\tto quickly create a Cobra application." + }, + "root-command-short-description": { + "description": "short description for the root command", + "hash": "sha1-a88cc9526d5e47f670ce9b089390e65415ab26cc", + "other": "A brief description of your application" + }, + "using-config-file": { + "description": "Message to indicate which config is being used", + "hash": "sha1-06fd9528f8226d1501ea09bc5d629f4922b4f3c6", + "other": "Using config file: {{.ConfigFileName}}" + } +} \ No newline at end of file diff --git a/locale/test-i18n-messages_test.go b/locale/test-i18n-messages_test.go new file mode 100644 index 0000000..d12c076 --- /dev/null +++ b/locale/test-i18n-messages_test.go @@ -0,0 +1,50 @@ +package locale_test + +import ( + "github.com/nicksnyder/go-i18n/v2/i18n" +) + +const ( + GrafficoSourceID = "github.com/snivilised/graffico" +) + +type GrafficoData struct{} + +func (td GrafficoData) SourceID() string { + return GrafficoSourceID +} + +// 🧊 Pavement Graffiti Report + +// PavementGraffitiReportTemplData +type PavementGraffitiReportTemplData struct { + GrafficoData + Primary string +} + +func (td PavementGraffitiReportTemplData) Message() *i18n.Message { + return &i18n.Message{ + ID: "pavement-graffiti-report.graffico.unit-test", + Description: "Report of graffiti found on a pavement", + Other: "Found graffiti on pavement; primary colour: '{{.Primary}}'", + } +} + +// ☒️ Wrong Source Id + +// WrongSourceIDTemplData +type WrongSourceIDTemplData struct { + GrafficoData +} + +func (td WrongSourceIDTemplData) SourceID() string { + return "FOO-BAR" +} + +func (td WrongSourceIDTemplData) Message() *i18n.Message { + return &i18n.Message{ + ID: "wrong-source-id.graffico.unit-test", + Description: "Incorrect Source Id for which doesn't match the one n the localizer", + Other: "Message with wrong id", + } +} diff --git a/locale/translate-defs.go b/locale/translate-defs.go new file mode 100644 index 0000000..3545fe1 --- /dev/null +++ b/locale/translate-defs.go @@ -0,0 +1,11 @@ +package locale + +// CLIENT-TODO: Should be updated to use url of the implementing project, +// so should not be left as astrolib. (this should be set by auto-check) +const AstrolibSourceID = "github.com/snivilised/astrolib" + +type astrolibTemplData struct{} + +func (td astrolibTemplData) SourceID() string { + return AstrolibSourceID +} diff --git a/resources/doc/i18n-README.md b/resources/doc/i18n-README.md new file mode 100644 index 0000000..b0d7469 --- /dev/null +++ b/resources/doc/i18n-README.md @@ -0,0 +1,72 @@ +# 🌐 i18n Assistance + +This document aims to augment that which appears in the [goi18n project](https://github.com/nicksnyder/go-i18n/) and provide information that can help the client use the i18n functionality defined there and its integration into this template project. The translations process is quite laborious, so this project tries alleviate this process by providing helper tasks which will be documented here also. + +## πŸ“ Directory Structure + +The local directory structure is as follows: + +- ___default___: contains the translation file created by the __newt__ (new translation task). Actually, this task creates an __active__ file (`active.en-GB.json`) in the `i18n/out` folder, the result of which needs to be manually copied into the __active__ file in the `default` folder. + +- ___deploy___: contains all the translations files that are intended to be deployed with the application. There will be one per supported language and by default this template project includes a translation file for __en-US__ (`astrolib.active.en-US.json`) + +## βš™οΈ Translation Workflow + +### ✨ New Translations + +__goi18n__ instructs the user to manually create an empty translation message file that they want to add (eg `translate.en-US.json`). This is taken care of by the __newt__ task. Then the requirement is to run the goi18n merge \ command (goi18n merge `astrolib.active.en-US.json` `astrolib.translate.en-US.json`). This has been wrapped up into the __merge__ task and the result is that the translation file `astrolib.translation.en-US.json` is populated with the messages to be translated. So the sequence goes: + +- run __newt__ task: (generates default language file `./src/i18n/out/active.en-GB.json` and empty `./src/i18n/out/us-US/astrolib.translation.en-US.json` file). This task can be run from the root folder, __goi18n__ will recursively search the directory tree for files with translate-able content, ie files with template definitions (___i18n.Message___) +- run __merge__ task: derives a translation file for the requested language __en-US__ using 2 files as inputs: source active file (`./src/i18n/out/active.en-GB.json`) and the empty __en-US__ translate file (`./src/i18n/out/us-US/astrolib.translation.en-US.json`), both of which were generated in the previous step. +- hand the translate file to your translator for them to translate +- rename the translate file to the active equivalent (`astrolib.translation.en-US.json`). Save this into the __deploy__ folder. This file will be deployed with the application. + +### 🧩 Update Existing Translations (⚠️ not finalised) + +__goi18n__ instructs the user to run the following steps: + +1. Run `goi18n extract` to update `active.en.toml` with the new messages. +2. Run `goi18n merge active.*.toml` to generate updated `translate.*.toml` files. +3. Translate all the messages in the `translate.*.toml` files. +4. Run `goi18n merge active.*.toml translate.*.toml` to merge the translated messages into the active message files. + +___The above description is way too vague and ambiguous. It is for this reason that this process has not been finalised. The intention will be to upgrade the instructions as time goes by and experience is gained.___ + +However, in this template, the user can execute the following steps: + +- run task __update__: this will re-extract messages into `active.en-GB.json` and then runs merge to create an updated translate file. +- hand the translate file to your translator for them to translate +- as before, this translated file should be used to update the active file `astrolib.active.en-US.json` inside the __deploy__ folder. + +## πŸŽ“ Task Reference + +❗This is a work in progress ... + +### πŸ’€ extract + +Scans the code base for messages and extracts them into __out/active.en-GB.json__ (The name of the file can't be controlled, it is derived from the language specified). No need to call this task directly. + +### πŸ’  newt + +Invokes the `extract` task to extract messages from code. After running this task, the __translate.en-US.json__ file is empty ready to be used as one of the inputs to the merge task (_why do we need an empty input file for the merge? Perhaps the input file instructs the language tag to goi18n_). + +Inputs: + +- source code + +Outputs: + +- ./src/i18n/out/active.en-GB.json (messages extracted from code, without hashes) +- ./src/i18n/out/en-US/translate.en-US.json (empty) + +### πŸ’  merge + +Inputs: + +- ./src/i18n/out/active.en-GB.json +- ./src/i18n/out/en-US/astrolib.translate-en-US.json + +Outputs: + +- ./src/i18n/out/active.en-US.json +- ./src/i18n/out/translate.en-US.json diff --git a/resources/images/go-logo-light-blue.png b/resources/images/go-logo-light-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..fcc24ede6f66b6dc77a61bd7fb3fbaf3d700702a GIT binary patch literal 15569 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 P`-7&Mj%tMpD)j#VxENu2 literal 0 HcmV?d00001 diff --git a/scripts/apply-coverage-exclusions.sh b/scripts/apply-coverage-exclusions.sh new file mode 100755 index 0000000..9f3c6d6 --- /dev/null +++ b/scripts/apply-coverage-exclusions.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# Define paths relative to the root directory +ROOT_DIR="$(git rev-parse --show-toplevel)" +COVERAGE_FILE="$ROOT_DIR/coverage.out" +EXCLUSIONS_FILE="$ROOT_DIR/scripts/coverage-exclusion-list.txt" + +# Check if required files exist +if [ ! -f "$COVERAGE_FILE" ]; then + echo "Error: Coverage file not found at $COVERAGE_FILE" + exit 1 +fi + +if [ ! -f "$EXCLUSIONS_FILE" ]; then + echo "Error: Exclusions file not found at $EXCLUSIONS_FILE" + exit 1 +fi + +# Create a temporary file +TEMP_FILE=$(mktemp) + +# Process the exclusions +while IFS= read -r line || [[ -n "$line" ]]; do + # Escape special characters in the line for use in sed + escaped_line=$(echo "$line" | sed 's/[\/&]/\\&/g') + + # Remove matching lines from the coverage file + sed "/${escaped_line}/d" "$COVERAGE_FILE" > "$TEMP_FILE" + + # Replace the original file with the modified content + mv "$TEMP_FILE" "$COVERAGE_FILE" +done < "$EXCLUSIONS_FILE" + +echo "Coverage exclusions have been applied successfully." diff --git a/scripts/automate-checklist.sh b/scripts/automate-checklist.sh new file mode 100644 index 0000000..c60f2c7 --- /dev/null +++ b/scripts/automate-checklist.sh @@ -0,0 +1,243 @@ +# shellcheck disable=SC2148 + +function auto-check() { + owner=$(git config --get remote.origin.url | cut -d '/' -f 4) + repo=$(git rev-parse --show-toplevel | xargs basename) + + echo "---> 😎 OWNER: $owner" + echo "---> 🧰 REPO: $repo" + echo "" + + if ! update-workflow-names "$repo" "$owner"; then + return 1 + fi + + if ! update-mod-file "$repo" "$owner"; then + return 1 + fi + + if ! update-source-id-variable-in-translate-defs "$repo" "$owner"; then + return 1 + fi + + + if ! update-astrolib-in-taskfile "$repo" "$owner"; then + return 1 + fi + + + if ! update-astrolib-in-goreleaser "$repo" "$owner"; then + return 1 + fi + + + if ! rename-templ-data-id "$repo" "$owner"; then + return 1 + fi + + + if ! update-import-statements "$repo" "$owner"; then + return 1 + fi + + + if ! update-readme "$repo" "$owner"; then + return 1 + fi + + + if ! rename-language-files "$repo" "$owner"; then + return 1 + fi + + + if ! reset-version; then + return 1 + fi + + touch ./.env + echo "βœ”οΈ done" + return 0 +} + +function update-all-generic() { + local title=$1 + local repo=$2 + local owner=$3 + local folder=$4 + local name=$5 + local target=$6 + local replacement=$7 + + echo " 🎯 ---> title: $title" + echo " βœ… ---> file pattern: $name" + echo " βœ… ---> folder: $folder" + echo " βœ… ---> target: $target" + echo " βœ… ---> replacement: $replacement" + + # !!!WARNING: sed + # does not work the same way between mac and linux + # + # find "$folder" -name "$name" -type f -print0: This part of the command uses the find utility to search for files within the specified folder ($folder) matching the given pattern ($name). Here's what each option does: + # -name "$name": Specifies the filename pattern to match. In this case, it matches the filenames with the pattern stored in the variable $name. + # -type f: Specifies that only regular files should be matched, excluding directories, symbolic links, etc. + # -print0: This option tells find to print the matched filenames separated by null characters (\0). This is essential for correctly handling filenames with spaces or special characters. + + # while IFS= read -r -d '' file; do: This initiates a while loop that reads each null-delimited filename (-d '') produced by the find command. Here's what each part does: + # IFS=: This sets the Internal Field Separator to nothing. This is to ensure that leading and trailing whitespace characters are not trimmed from each filename. + # read -r: This command reads input from the pipe (|) without interpreting backslashes (-r option). + # -d '': This option specifies the delimiter as null characters (\0), ensuring that filenames containing spaces or special characters are read correctly. + # file: This is the variable where each filename from the find command is stored during each iteration of the loop. + # + # trying to modify: + # - ci-workflow.yml + # - release-workflow.yml + find "$folder" -name "$name" -type f -print0 | while IFS= read -r -d '' file; do + echo "Processing file: $file" + uname_output=$(uname) + # sed help: + # '': no file backup needed (we modify the file in place without backing up original) + # but note that this is only required for mac. for linux, you dont need the ''. + # + # -i: The in-place edit flag, which tells sed to modify the original file inline. + # 's/search_pattern/replacement_text/g': + # + # s: Indicates that this is a substitution command. + # /search_pattern/: The pattern to search for. + # /replacement_text/: The text to replace the search pattern with. + # g: The global flag, which ensures that all occurrences of the + # search pattern are replaced, not just the first one. + if [[ "$uname_output" == *"Darwin"* ]]; then + if ! sed -i '' "s/${target}/${replacement}/g" "$file"; then + echo "!!! β›” Sed on mac failed for $file" + return 1 + fi + else + if ! sed -i "s/${target}/${replacement}/g" "$file"; then + echo "!!! β›” Sed on linux failed for $file" + return 1 + fi + fi + done + + echo " βœ”οΈ ---> DONE" + echo "" + return 0 +} + +function update-mod-file() { + local repo=$1 + local owner=$2 + local folder=./ + local file_pattern=go.mod + local target="module github.com\/snivilised\/astrolib" + local replacement="module github.com\/$owner\/$repo" + update-all-generic "update-mod-file" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement" +} + +function update-source-id-variable-in-translate-defs() { + local repo=$1 + local owner=$2 + local folder=./i18n/ + local file_pattern=translate-defs.go + local target="AstrolibSourceID" + tc_repo=$(echo "${repo:0:1}" | tr '[:lower:]' '[:upper:]')${repo:1} + local replacement="${tc_repo}SourceID" + update-all-generic "update-source-id-variable-in-translate-defs" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement" +} + +function update-astrolib-in-taskfile() { + local repo=$1 + local owner=$2 + local folder=./ + local file_pattern=Taskfile.yml + local target=astrolib + local replacement=$repo + update-all-generic "update-astrolib-in-taskfile" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement" +} + +function update-workflow-names() { + local repo=$1 + local owner=$2 + local folder=.github/workflows + local file_pattern="*.yml" + local target="name: Astrolib" + tc_repo=$(echo "${repo:0:1}" | tr '[:lower:]' '[:upper:]')${repo:1} + local replacement="name: $tc_repo" + update-all-generic "πŸ’₯ update-workflow-names" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement" +} + +function update-astrolib-in-goreleaser() { + local repo=$1 + local owner=$2 + local folder=./ + local file_pattern=.goreleaser.yaml + local target=astrolib + local replacement=$repo + update-all-generic "update-astrolib-in-goreleaser" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement" +} + +function rename-templ-data-id() { + local repo=$1 + local owner=$2 + local folder=./ + local file_pattern="*.go" + local target="astrolibTemplData" + local replacement="${repo}TemplData" + update-all-generic "rename-templ-data-id" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement" +} + +function update-readme() { + local repo=$1 + local owner=$2 + local folder=./ + local file_pattern=README.md + local target="astrolib: " + local replacement="${repo}: " + + + if ! update-all-generic "update-readme(astrolib:)" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement"; then + return 1 + fi + + target="snivilised\/astrolib" + replacement="$owner\/$repo" + + if ! update-all-generic "update-readme(snivilised/astrolib)" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement"; then + return 1 + fi + + target="astrolib Continuous Integration" + tc_repo=$(echo "${repo:0:1}" | tr '[:lower:]' '[:upper:]')${repo:1} + replacement="$tc_repo Continuous Integration" + + if ! update-all-generic "update-readme(astrolib Continuous Integration)" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement"; then + return 1 + fi + + return 0 +} + +function update-import-statements() { + local repo=$1 + local owner=$2 + local folder=./ + local file_pattern="*.go" + local target="snivilised\/astrolib" + local replacement="$owner\/$repo" + update-all-generic "update-import-statements" "$repo" "$owner" "$folder" "$file_pattern" "$target" "$replacement" +} + +function rename-language-files() { + local repo=$1 + find . -name 'astrolib*.json' -type f -print0 | + while IFS= read -r -d '' file; do + mv "$file" "$(dirname "$file")/$(basename "$file" | sed "s/^astrolib/$repo/")" + done + return $? +} + +function reset-version() { + echo "v0.1.0" > ./VERSION + return 0 +} diff --git a/scripts/coverage-exclusion-list.txt b/scripts/coverage-exclusion-list.txt new file mode 100644 index 0000000..fa1a3ad --- /dev/null +++ b/scripts/coverage-exclusion-list.txt @@ -0,0 +1 @@ +github.com/snivilised/astrolib/internal/third diff --git a/scripts/git-workflow.gum.sh b/scripts/git-workflow.gum.sh new file mode 100644 index 0000000..092078f --- /dev/null +++ b/scripts/git-workflow.gum.sh @@ -0,0 +1,537 @@ +#!/usr/bin/env bash + +# use https://coolors.co to help create colur schemes +# glyphs: +# - https://github.com/ryanoasis/nerd-fonts/wiki/Glyph-Sets-and-Code-Points +# - https://b.agaric.net/page/agave +# - https://github.com/blobject/agave +# - https://www.nerdfonts.com/cheat-sheet + +# +# +# +# +# +# +# +# use a red/pink colour for error: f72585 + +# some snippets: +# +# gum confirm "Commit changes?" && git commit -m "$SUMMARY" -m "$DESCRIPTION" +# +# This series of colours does not POP enough +# +# gum style "Hello, there! Welcome to $(gum style --foreground "#85c0ff" 'Jordy Blue')." +# gum style "Hello, there! Welcome to $(gum style --foreground "#94b0da" 'Powder Blue')." +# gum style "Hello, there! Welcome to $(gum style --foreground "#8f91a2" 'Cool gray')." +# gum style "Hello, there! Welcome to $(gum style --foreground "#505a5b" 'Davys gray')." +# gum style "Hello, there! Welcome to $(gum style --foreground "#343f3e" 'Outer space')." + +_col_promt="#fbf8cc" +_col_remedy="#fde4cf" +_col_error="#ffcfd2" +_col_branch="#f1c0e8" +_col_action="#cfbaf0" +_col_item="#a3c4f3" +_col_query="#98f5e1" +_col_git="#b9fbc0" +_col_msg="#90dbf4" +_col_affirm="#8eecf5" + +# common emojis: +# πŸ˜• 😎 +# 🍭 πŸŽ€ 🎁 +# βœ… +# πŸš€ πŸ”† +# πŸ₯₯ +# πŸ”₯ β›” ❌ +# + +# pastello pallette +# +# +# +# +# +# +# +# +# +# +# +# +# + + +# 🍭 gum utility +# + +function _text() { + # when you call this function, the colour must be inside quotes, eg: + # _text greetings "#98f5e1" + # + text=$1 + colour=$2 + gum style --foreground "$colour" "$text" +} + +function _a() { # action + text=$1 + colour=#cfbaf0 + _text "$text" "$colour" +} + +function _b() { # branch + text=$1 + colour=#f1c0e8 + _text "$text" "$colour" +} + +function _e() { # error + text=$1 + colour=#f63e02 + _text "β›” $text" "$colour" +} + +function _g() { # git-command + text=$1 + colour=#b9fbc0 + _text "ξ‚  $text" "$colour" +} + +function _i() { # item + text=$1 + colour=#a3c4f3 + _text "$text" "$colour" +} + +function _m() { # msg + text=$1 + colour=#90dbf4 + _text "$text" "$colour" +} + +function _o() { # ok + text=$1 + colour=#94fbab + _text "βœ… $text" "$colour" +} + +function _p() { # prompt + text=$1 + colour=#fbf8cc + _text "πŸ˜• $text" "$colour" +} + +function _q() { # query + text=$1 + colour=#98f5e1 + _text "$text" "$colour" +} + +function _r() { # remedy + text=$1 + colour=#fde4cf + _text "$text" "$colour" +} + +function _w() { # warning + text=$1 + colour=#ffcfd2 + _text "$text" "$colour" +} + + +# 🍯 git dev workflow commands; This script makes use of nerdfonts.com glyphs, eg ξ‚  +# + +# === πŸ₯₯ git-operations ======================================================== + +function get-def-branch() { + echo main +} + +function gad() { + if [ -z "$(git status -s -uno | grep -v '^ ' | awk '{print $2}')" ]; then + gum confirm "$(_text 'Stage all?' $_col_query)" && git add . + fi + + return 0 +} + +function get-tracking-branch() { + git config "branch.$(git_current_branch).remote" +} + +function check-upstream-branch() { + upstream_branch=$(get-tracking-branch) + feature_branch=$(git_current_branch) + + if [ -z "$upstream_branch" ]; then + # echo "===> πŸ› No upstream branch detected for : 'πŸŽ€ $feature_branch'" + gum style "===> πŸ› No upstream branch detected for : πŸŽ€ $(_text "$feature_branch" $_col_item)" + + if ! _prompt-are-you-sure; then + return 1 + fi + fi + + return 0 +} + +# +# 🍭 end gum utility + +# === πŸ₯₯ interactive-rebase ==================================================== + +function _do_start-interactive-rebase() { + num_commits=$(($1)) + + git rebase -i HEAD~"$num_commits" +} + +function start-rebase() { + feature_branch=$(git branch --show-current) + num_commits=$(git log main.."$feature_branch" --pretty=oneline | wc -l) + minimum=2 + display_commits="$num_commits" + + if [[ $num_commits -lt $minimum ]]; then + # whenever time permits, this could be expanded to list all + # the commits that are to be squashed and put them into + # a bubbles table. + # + gum style "$(_e "Not enought commits ($(_i "$display_commits")) " \ + "for rebase on branch '$(_b "$feature_branch")', Aborted!")" + + return 1 + fi + + _prompt-are-you-sure-with-context \ + "$(gum style "found $(_i "$display_commits") commits on branch '$(_b "$feature_branch")'")" && \ + (_do_start-interactive-rebase "$num_commits" || gum style "$(_e "Aborted!")") + + return 0 +} + +# === πŸ₯₯ gcan(git-commit-amend-no-edit) ======================================== + +function gcan() { + feature_branch=$(git branch --show-current) + num_commits=$(git log main.."$feature_branch" --pretty=oneline | wc -l) + minimum=1 + display_commits="$num_commits" + + if [[ $num_commits -lt $minimum ]]; then + gum style "$(_e "Not enought commits, for commit amend on branch '$(_b "$feature_branch")', Aborted!")" + + return 1 + fi + + last=$(git log -n 1 --pretty=format:"%s (HASH: '%h')" "$feature_branch") + + _prompt-are-you-sure-with-context \ + gum style "last commit: $(_i "$last")" && \ + git commit --amend --no-edit -v +} + +# === πŸ₯₯ prompt ================================================================ + +function _prompt() { + message="$1" + gum confirm "$(_m "$message")" + + return $? +} + +function _prompt-are-you-sure { + _prompt "are you sure? πŸ‘Ύ" + result=$? + + if [ ! "$result" -eq 0 ]; then + gum style "$(_e "Aborted!")" + fi + + return $result +} + +function _prompt-are-you-sure-with-context { + context="$1" + + _prompt "$context, are you sure? πŸ‘Ύ" + result=$? + + if [ ! "$result" -eq 0 ]; then + gum style "$(_e "Aborted!")" + fi + + return $result +} + +# === πŸ₯₯ start-feat ============================================================ + +function start-feat() { + feature_branch=$1 + + if [[ -n $1 ]]; then + gum style "===> πŸš€ $(_a 'START FEATURE'): 'πŸŽ€ $(_b "$feature_branch")'" + + git checkout -b "$1" + else + # echo "!!! πŸ˜• Please specify a feature branch" + gum style "!!! $(_r 'Please specify a feature branch')" + + return 1 + fi + + return 0 +} + +# === πŸ₯₯ end-feat ============================================================== + +function _do-end-feat() { + feature_branch=$(git_current_branch) + default_branch=$(get-def-branch) + + if _prompt "About to end feature 🎁 '$feature_branch' ... have you squashed commits"; then + gum style "<=== ✨ $(_a 'END FEATURE'): $(_b "$feature_branch")" + + if [ "$feature_branch" != master ] && [ "$feature_branch" != main ]; then + git branch --unset-upstream + # can't reliably use prune here, because we have in effect + # a race condition, depending on how quickly github deletes + # the upstream branch after Pull Request "Rebase and Merge" + # + # gcm && git fetch --prune + # have to wait a while and perform the prune or delete + # local branch manually. + # + git checkout "$default_branch" + git pull origin "$default_branch" + + gum style "$(_o 'Done!')" + else + # echo "!!! πŸ˜• Not on a feature branch ($feature_branch)" + gum style "!!! $(_e 'Not on a feature branch') ($(_b "$feature_branch"))" + + return 1 + fi + else + gum style "$(_e "Aborted!")" + + return 1 + fi + + return 0 +} + +function end-feat() { + _prompt-are-you-sure && _do-end-feat +} + +# === πŸ₯₯ push-feat ============================================================= + +function _do-push-feat() { + current_branch=$(git_current_branch) + default_branch=$(get-def-branch) + + if [ "$current_branch" = "$default_branch" ]; then + # echo "!!! β›” Aborted! still on default branch($default_branch) branch ($current_branch)" + gum style "!!! $(_e 'Aborted! still on default branch') " \ + "($(_b "$default_branch")) branch ($(_b "$current_branch"))" + + return 1 + fi + + if ! git push origin --set-upstream "$current_branch"; then + # echo "!!! β›” Aborted! Failed to push feature for branch: $current_branch" + gum style "!!! $(_e 'Aborted! Failed to push feature for branch:') " \ + "$(_b "$current_branch")" + + return 1 + fi + + gum style "$(_o 'pushed feature to') $(_b "$current_branch") ok!" + + return 0 +} + +function push-feat() { + _prompt-are-you-sure && _do-push-feat +} + +# === πŸ₯₯ check-tag ============================================================= + +function check-tag() { + rel_tag=$1 + if ! [[ $rel_tag =~ ^[0-9] ]]; then + # echo "!!! β›” Aborted! invalid tag" + gum style "!!! $(_e 'Aborted! invalid tag')" + + return 1 + fi + + return 0 +} + +# === πŸ₯₯ do-release ============================================================ + +function _do-release() { + if [[ -n $1 ]]; then + if ! check-tag "$1"; then + return 1 + fi + + raw_version=$1 + version_number=v$1 + current_branch=$(git_current_branch) + default_branch=$(get-def-branch) + + if [[ $raw_version == v* ]]; then + # the # in ${raw_version#v} is a parameter expansion operator + # that removes the shortest match of the pattern "v" from the beginning + # of the string variable. + # + version_number=$raw_version + raw_version=${raw_version#v} + fi + + if [ "$current_branch" != "$default_branch" ]; then + # echo "!!! β›” Aborted! not on default($default_branch) branch; current($current_branch)" #error/branch + + gum style "!!! $(_e 'Aborted! not on default')($(_b "$default_branch")) " \ + "branch; current($(_b "$current_branch"))" + + return 1 + fi + + # echo "===> πŸš€ START RELEASE: '🎁 $version_number'" + gum style "===> πŸš€ $(_a 'START RELEASE'): '🎁 $(_i "$version_number")'" + release_branch="release/$version_number" + + if ! git checkout -b "$release_branch"; then + # echo "!!! β›” Aborted! Failed to create the release branch: $release_branch" #error/branch + gum style "!!! $(_e 'Aborted! Failed to create the release branch:') $(_b "$release_branch")" + + return 1 + fi + + if [ -e ./VERSION ]; then + if ! echo "$version_number" > ./VERSION; then + # echo "!!! β›” Aborted! Failed to update VERSION file" #error/item + gum style "!!! $(_e 'Aborted! Failed to update VERSION file')" + + return 1 + fi + + + if ! git add ./VERSION; then + # echo "!!! Aborted! Failed to git add VERSION file" #error/item + gum style "$(_e "!!! Aborted! Failed to git add $(_i 'VERSION') file")" + + return 1 + fi + + if [ -e ./VERSION-PATH ]; then + version_path=$(more ./VERSION-PATH) + echo "$raw_version" > "$version_path" + + if ! git add "$version_path"; then + # echo "!!! β›” Aborted! Failed to git add VERSION-PATH file ($version_path)" #error/item + gum style "!!! $(_e 'Aborted! Failed to git add VERSION-PATH file') ($("_i $version_path"))" + + return 1 + fi + fi + + if ! git commit -m "Bump version to $version_number"; then + # echo "!!! β›” Aborted! Failed to commit VERSION file" #error/item + gum style "!!! $(_e 'Aborted! Failed to commit VERSION file')" + + return 1 + fi + + if ! git push origin --set-upstream "$release_branch"; then + # echo "!!! β›” Aborted! Failed to push release $version_number upstream" #error/item + gum style "$(e "!!! Aborted! Failed to push release $(_i "$version_number") upstream")" + + return 1 + fi + + # echo "Done! βœ…" + gum style "$(_o 'Done!')" + else + # echo "!!! β›” Aborted! VERSION file is missing. (In root dir?)" #error + gum style "!!! $(_e "Aborted! $(_i 'VERSION') file is missing. (In root dir?)")" + + return 1 + fi + else + # echo "!!! πŸ˜• Please specify a semantic version to release" #remedy + gum style "!!! $(_r 'Please specify a semantic version to release')" + + return 1 + fi + + return 0 +} + +# release , !!! do not specify the v prefix, added automatically +# should be run from the root directory otherwise relative paths won't work properly. +function release() { + _prompt-are-you-sure && _do-release "$1" +} + +# === πŸ₯₯ tag-rel =============================================================== + +function _do-tag-rel() { + if [[ -n "$1" ]]; then + version_number="v$1" + current_branch=$(git_current_branch) + default_branch=$(get-def-branch) + + if [ "$current_branch" != "$default_branch" ]; then + # echo "!!! β›” Aborted! not on default($default_branch) branch; current($current_branch)" #error/branch + gum style "$(_e "!!! Aborted! not on default($(_b "$default_branch")) " \ + "branch; current($(_b "$current_branch"))")" + + return 1 + fi + + gum style "$(_a "===> 🏷️ PUSH TAG: '$(_i "$version_number")'")" + + if ! git tag -a "$version_number" -m "Release $version_number"; then + # echo "!!! β›” Aborted! Failed to create annotated tag: $version_number" #error + gum style "$(_e "!!! Aborted! Failed to create annotated tag: $(_i "$version_number")")" + + return 1 + fi + + + if ! git push origin "$version_number"; then + # echo "!!! β›” Aborted! Failed to push tag: $version_number" #error + gum style "$(_e "!!! Aborted! Failed to push tag: $(_i "$version_number")")" + + return 1 + fi + + # echo "Done! βœ…" + gum style "$(_o 'Done!')" + else + # echo "!!! πŸ˜• Please specify a release semantic version to tag" # remedy + gum style "$(_r '!!! Please specify a release semantic version to tag')" + + reurn 1 + fi + + return 0 +} + +# tag-rel , !!! do not specify the v prefix, added automatically +function tag-rel() { + _prompt-are-you-sure && _do-tag-rel "$1" +} + +# +# 🍯 end git dev workflow commands diff --git a/scripts/git-workflow.zshrc b/scripts/git-workflow.zshrc new file mode 100644 index 0000000..8c61c3e --- /dev/null +++ b/scripts/git-workflow.zshrc @@ -0,0 +1,221 @@ + +get-def-branch() { + echo main +} + +are-you-sure() { + echo "πŸ‘Ύ Are you sure❓ (type 'y' to confirm)" + read squashed + + if [ $squashed = "y" ]; then + return 0 + else + echo "β›” Aborted!" + return 1 + fi +} + +startfeat() { + if [[ -n $1 ]]; then + echo "===> πŸš€ START FEATURE: 'πŸŽ€ $1'" + git checkout -b $1 + return 0 + else + echo "!!! πŸ˜• Please specify a feature branch" + fi + return 1 +} + +endfeat() { + if ! are-you-sure $1; then + return 1 + fi + + local feature_branch=$(git_current_branch) + local default_branch=$(get-def-branch) + + echo "About to end feature 🎁 '$feature_branch' ... have you squashed commits? (type 'y' to confirm)" + read squashed + + if [ $squashed = "y" ]; then + echo "<=== ✨ END FEATURE: '$feature_branch'" + + if [ $feature_branch != master ] && [ $feature_branch != main ]; then + git branch --unset-upstream + # can't reliably use prune here, because we have in effect + # a race condition, depending on how quickly github deletes + # the upstream branch after Pull Request "Rebase and Merge" + # + # gcm && git fetch --prune + # have to wait a while and perform the prune or delete + # local branch manually. + # + git checkout $default_branch + git pull origin $default_branch + echo "Done! βœ”οΈ" + else + echo "!!! πŸ˜• Not on a feature branch ($feature_branch)" + fi + else + echo "β›” Aborted!" + fi +} + +push-feat() { + if ! are-you-sure $1; then + return 1 + fi + + local current_branch=$(git_current_branch) + local default_branch=$(get-def-branch) + + if [ $current_branch = $default_branch ]; then + echo "!!! β›” Aborted! still on default branch($default_branch) branch ($current_branch)" + return 1 + fi + + git push origin --set-upstream $current_branch + if [ $? -ne 0 ]; then + echo "!!! β›” Aborted! Failed to push feature for branch: $current_branch" + return 1 + fi + + echo "pushed feature to $current_branch, ok! βœ”οΈ" + return 0 +} + +function check-tag() { + local rel_tag=$1 + if ! [[ $rel_tag =~ ^[0-9] ]]; then + echo "!!! β›” Aborted! invalid tag" + return 1 + fi + return 0 +} + +# release , !!! do not specify the v prefix, added automatically +# should be run from the root directory otherwise relative paths won't work properly. +function release() { + if ! are-you-sure $1; then + return 1 + fi + + if [[ -n $1 ]]; then + if ! check-tag $1; then + return 1 + fi + + # these string initialisers should probably e changed, don't + # need the surrounding quotes, but it works so why fiddle? + # + local raw_version=$1 + local version_number=v$1 + local current_branch=$(git_current_branch) + local default_branch=$(get-def-branch) + + if [[ $raw_version == v* ]]; then + # the # in ${raw_version#v} is a parameter expansion operator + # that removes the shortest match of the pattern "v" from the beginning + # of the string variable. + # + version_number=$raw_version + raw_version=${raw_version#v} + fi + + if [ $current_branch != $default_branch ]; then + echo "!!! β›” Aborted! not on default($default_branch) branch; current($current_branch)" + return 1 + fi + + echo "===> πŸš€ START RELEASE: '🎁 $version_number'" + local release_branch=release/$version_number + + git checkout -b $release_branch + if [ $? -ne 0 ]; then + echo "!!! β›” Aborted! Failed to create the release branch: $release_branch" + return 1 + fi + + if [ -e ./VERSION ]; then + echo $version_number > ./VERSION + if [ $? -ne 0 ]; then + echo "!!! β›” Aborted! Failed to update VERSION file" + return 1 + fi + + git add ./VERSION + if [ $? -ne 0 ]; then + echo "!!! β›” Aborted! Failed to git add VERSION file" + return + fi + + if [ -e ./VERSION-PATH ]; then + local version_path=$(more ./VERSION-PATH) + echo $raw_version > $version_path + git add $version_path + if [ $? -ne 0 ]; then + echo "!!! β›” Aborted! Failed to git add VERSION-PATH file ($version_path)" + return + fi + fi + + git commit -m "Bump version to $version_number" + if [ $? -ne 0 ]; then + echo "!!! β›” Aborted! Failed to commit VERSION file" + return 1 + fi + + git push origin --set-upstream $release_branch + if [ $? -ne 0 ]; then + echo "!!! β›” Aborted! Failed to push release $version_number upstream" + return 1 + fi + + echo "Done! βœ”οΈ" + return 0 + else + echo "!!! β›” Aborted! VERSION file is missing. (In root dir?)" + fi + else + echo "!!! πŸ˜• Please specify a semantic version to release" + fi + return 1 +} + +# push-tag , !!! do not specify the v prefix, added automatically +tag-rel() { + if ! are-you-sure $1; then + return 1 + fi + + if [[ -n $1 ]]; then + local version_number="v$1" + local current_branch=$(git_current_branch) + local default_branch=$(get-def-branch) + + if [ $current_branch != $default_branch ]; then + echo "!!! β›” Aborted! not on default($default_branch) branch; current($current_branch)" + return 1 + fi + + echo "===> 🏷️ PUSH TAG: '$version_number'" + + git tag -a $version_number -m "Release $version_number" + if [ $? -ne 0 ]; then + echo "!!! β›” Aborted! Failed to create annotated tag: $version_number" + return 1 + fi + + git push origin $version_number + if [ $? -ne 0 ]; then + echo "!!! β›” Aborted! Failed to push tag: $version_number" + return 1 + fi + + echo "Done! βœ”οΈ" + return 0 + else + echo "!!! πŸ˜• Please specify a release semantic version to tag" + fi + return 1 +} diff --git a/test/data/l10n/test.astrolib.active.en-US.json b/test/data/l10n/test.astrolib.active.en-US.json new file mode 100644 index 0000000..cdca24b --- /dev/null +++ b/test/data/l10n/test.astrolib.active.en-US.json @@ -0,0 +1,7 @@ +{ + "foo-bar.astrolib.unit-test": { + "description": "foo bar", + "hash": "sha1-d53a205a336e07cf9eac45471b3870f9489288ec", + "other": "foo bar" + } +} \ No newline at end of file diff --git a/test/data/l10n/test.graffico.active.en-US.json b/test/data/l10n/test.graffico.active.en-US.json new file mode 100644 index 0000000..fbf0305 --- /dev/null +++ b/test/data/l10n/test.graffico.active.en-US.json @@ -0,0 +1,7 @@ +{ + "pavement-graffiti-report.graffico.unit-test": { + "description": "Report of graffiti found on a pavement", + "hash": "sha1-69c428d6be2dc281d00f095218e1ed7671d1c984", + "other": "Found graffiti on sidewalk; primary color: '{{.Primary}}'" + } +} \ No newline at end of file