From 8fa14bc7382e5778dda00416e9b705b32efd823d Mon Sep 17 00:00:00 2001 From: Agaba-derrick Date: Mon, 2 Nov 2026 14:40:29 +0300 Subject: [PATCH] feat(cli): make MIT license compliant (#545) - Replace yeqown/go-qrcode/writer/standard with custom stdlib qrcode.Writer - Embed qr_logo.png in internal/cmd package for portable execution - Purge fogleman/gg and golang/freetype GPL dependencies via go mod tidy - Update synkronus-cli/LICENSE back to MIT - Update licensing notices in READMEs, docs, and THIRD_PARTY_NOTICES - Add unit tests for FRMLS encoding and PNG QR generation --- README.md | 4 +- THIRD_PARTY_NOTICES.md | 21 +- docs/docs/reference/synkronus-cli.md | 2 +- synkronus-cli/AGENTS.md | 2 +- synkronus-cli/LICENSE | 375 ++---------------------- synkronus-cli/README.md | 11 +- synkronus-cli/go.mod | 5 - synkronus-cli/go.sum | 27 -- synkronus-cli/internal/cmd/qr.go | 10 +- synkronus-cli/internal/cmd/qr_logo.png | Bin 0 -> 35877 bytes synkronus-cli/internal/cmd/qr_test.go | 109 +++++++ synkronus-cli/internal/cmd/qr_writer.go | 132 +++++++++ 12 files changed, 278 insertions(+), 420 deletions(-) create mode 100644 synkronus-cli/internal/cmd/qr_logo.png create mode 100644 synkronus-cli/internal/cmd/qr_test.go create mode 100644 synkronus-cli/internal/cmd/qr_writer.go diff --git a/README.md b/README.md index 1d1866856..73a212370 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,6 @@ irm https://raw.githubusercontent.com/OpenDataEnsemble/ode/main/scripts/install- curl -fsSL https://raw.githubusercontent.com/OpenDataEnsemble/ode/main/scripts/install-synkronus-cli.sh | bash ``` -**License note:** this component is currently **GPL-2.0-or-later** (see [synkronus-cli/LICENSE](synkronus-cli/LICENSE)) while the QR PNG stack depends on GPL-classified libraries. We plan to swap in a stdlib-only renderer and return the CLI to **MIT**. See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). - ### **synkronus-portal** A web-based admin interface for Synkronus. It covers the same tasks as **synkronus-cli** in the browser. @@ -150,7 +148,7 @@ CI will **fail** if: ## License -The repository root is [MIT](./LICENSE). **synkronus-cli** is **GPL-2.0-or-later** until the QR dependency cleanup above is done. Third-party and mixed-component details: [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md). +The repository is licensed under the [MIT License](./LICENSE). Third-party and component details: [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md). ## Get Involved diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index b517b1178..f5d26e122 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -1,6 +1,6 @@ # Third-party notices -Open Data Ensemble (ODE) primarily uses the [MIT License](./LICENSE) at the repository root. **`synkronus-cli`** is an exception: it is **GPL-2.0-or-later** until the QR stack is refactored (see below). This file summarizes **other** separately licensed open-source components and points to **SBOMs** for a full machine-readable list. +Open Data Ensemble (ODE) ships with **your own code** under the [MIT License](./LICENSE). This file summarizes **separately licensed open-source components** that may be included when you build and distribute ODE software, and points to where to find a **machine-readable** full list. > **This is not legal advice.** For distribution (especially mobile apps and CLI binaries), have your process reviewed against your policies. @@ -33,24 +33,13 @@ Production JavaScript dependency licenses are **overwhelmingly permissive** (e.g `synkronus` and `synkronus-cli` SBOMs (`synkronus.cdx.json`, `synkronus-cli.cdx.json`) list modules and detected licenses. -### `synkronus-cli` license (GPL-2.0-or-later) +### `synkronus-cli` licensing -The **synkronus-cli** component is explicitly licensed under **GNU GPL v2 or -later**; see [`synkronus-cli/LICENSE`](./synkronus-cli/LICENSE). That aligns with -the current QR PNG stack: +The **synkronus-cli** component is licensed under the **MIT License**; see [`synkronus-cli/LICENSE`](./synkronus-cli/LICENSE). -**`yeqown/go-qrcode/writer/standard`** → **`github.com/fogleman/gg`** → -**`github.com/golang/freetype`** (detectors report **GPL-2.0-or-later**). +The QR PNG generation path uses a custom standard library-based `qrcode.Writer` (`image`, `image/draw`, `image/png`), avoiding copyleft/GPL dependency chains (`fogleman/gg` / `golang/freetype`). -The rest of the monorepo may remain under other licenses (e.g. MIT at the -repo root). - -**Follow-up:** replace `writer/standard` with a **stdlib-only** `qrcode.Writer`, -drop `gg` / freetype from the module graph, then **re-license synkronus-cli -to MIT** if desired. Ticket-ready notes: -[`synkronus-cli/FOLLOWUP-custom-qrcode-writer.md`](./synkronus-cli/FOLLOWUP-custom-qrcode-writer.md). - -`synkronus` server SBOMs checked in this audit did **not** show GPL/AGPL/LGPL identifiers in CycloneDX output; still rely on the SBOM for each release. +`synkronus` and `synkronus-cli` SBOMs checked in this audit did **not** show GPL/AGPL/LGPL identifiers in CycloneDX output; still rely on the SBOM for each release. ## Vendored native sources (Formulus / Android) diff --git a/docs/docs/reference/synkronus-cli.md b/docs/docs/reference/synkronus-cli.md index 8a0c279ef..30cccec32 100644 --- a/docs/docs/reference/synkronus-cli.md +++ b/docs/docs/reference/synkronus-cli.md @@ -659,7 +659,7 @@ synk: command not found ## License -The Synkronus CLI is currently **GPL-2.0-or-later** while it depends on GPL-classified QR code libraries. The plan is to swap in stdlib-only rendering and return to MIT license. See `synkronus-cli/FOLLOWUP-custom-qrcode-writer.md` and `THIRD_PARTY_NOTICES.md` for details. +The Synkronus CLI is licensed under the **MIT License**. See `synkronus-cli/LICENSE` and `THIRD_PARTY_NOTICES.md` for details. ## Related Content diff --git a/synkronus-cli/AGENTS.md b/synkronus-cli/AGENTS.md index a449ed846..1a12d107e 100644 --- a/synkronus-cli/AGENTS.md +++ b/synkronus-cli/AGENTS.md @@ -23,7 +23,7 @@ ## License note -The CLI may be **GPL-2.0-or-later** until the QR dependency cleanup described in the root [README.md](../README.md) and [FOLLOWUP-custom-qrcode-writer.md](FOLLOWUP-custom-qrcode-writer.md). **Do not** change license without maintainer intent. +The CLI is licensed under **MIT** (see [`LICENSE`](LICENSE)). --- diff --git a/synkronus-cli/LICENSE b/synkronus-cli/LICENSE index 9341e9649..95674de4a 100644 --- a/synkronus-cli/LICENSE +++ b/synkronus-cli/LICENSE @@ -1,354 +1,21 @@ -Synkronus CLI — project licensing -Copyright (C) 2026 Open Data Ensemble - -The **synkronus-cli** program in this repository is licensed under the -GNU General Public License **version 2 or later** (GPL-2.0-or-later), in -part because current releases link material that is offered under -GPL-2.0-or-later (see `THIRD_PARTY_NOTICES.md` at the repository root and -release SBOMs). - -Other directories in the same monorepo may remain under different -licenses (e.g. MIT). This file applies to **synkronus-cli** as a component. - -SPDX-License-Identifier: GPL-2.0-or-later - ------------------------------------------------------------------------- - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, see . - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Moe Ghoul, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. +MIT License + +Copyright (c) 2026 Open Data Ensemble + +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/synkronus-cli/README.md b/synkronus-cli/README.md index 3b33a62c4..f64977b9a 100644 --- a/synkronus-cli/README.md +++ b/synkronus-cli/README.md @@ -193,16 +193,7 @@ synk data export ./backups/observations_parquet.zip ## License -**GPL-2.0-or-later** — see [`LICENSE`](./LICENSE) in this directory. - -The rest of the ODE monorepo may use other licenses (for example MIT at the -repository root). This applies to **synkronus-cli** only. - -**Planned follow-up:** replace the `yeqown/go-qrcode/writer/standard` (gg / -freetype) stack with a small stdlib `qrcode.Writer` implementation so this -component can return to a permissive license. See -[`FOLLOWUP-custom-qrcode-writer.md`](./FOLLOWUP-custom-qrcode-writer.md) for a -ticket-ready summary. +MIT — see [`LICENSE`](./LICENSE) in this directory. ## Dev. notes diff --git a/synkronus-cli/go.mod b/synkronus-cli/go.mod index 8ccd81309..780a1409a 100644 --- a/synkronus-cli/go.mod +++ b/synkronus-cli/go.mod @@ -13,7 +13,6 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 github.com/yeqown/go-qrcode/v2 v2.2.5 - github.com/yeqown/go-qrcode/writer/standard v1.3.0 golang.org/x/term v0.32.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -21,12 +20,10 @@ require ( require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect - github.com/fogleman/gg v1.3.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getkin/kin-openapi v0.133.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -40,7 +37,6 @@ require ( github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect @@ -56,7 +52,6 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/image v0.10.0 // indirect golang.org/x/mod v0.23.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.33.0 // indirect diff --git a/synkronus-cli/go.sum b/synkronus-cli/go.sum index ff0acc27d..34c748a3f 100644 --- a/synkronus-cli/go.sum +++ b/synkronus-cli/go.sum @@ -15,8 +15,6 @@ github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aT github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= -github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -34,8 +32,6 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -110,8 +106,6 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6 github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -164,12 +158,9 @@ github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIj github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds= github.com/yeqown/go-qrcode/v2 v2.2.5 h1:HCOe2bSjkhZyYoyyNaXNzh4DJZll6inVJQQw+8228Zk= github.com/yeqown/go-qrcode/v2 v2.2.5/go.mod h1:uHpt9CM0V1HeXLz+Wg5MN50/sI/fQhfkZlOM+cOTHxw= -github.com/yeqown/go-qrcode/writer/standard v1.3.0 h1:chdyhEfRtUPgQtuPeaWVGQ/TQx4rE1PqeoW3U+53t34= -github.com/yeqown/go-qrcode/writer/standard v1.3.0/go.mod h1:O4MbzsotGCvy8upYPCR91j81dr5XLT7heuljcNXW+oQ= github.com/yeqown/reedsolomon v1.0.0 h1:x1h/Ej/uJnNu8jaX7GLHBWmZKCAWjEJTetkqaabr4B0= github.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw/Iy6g1WiiM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= @@ -177,14 +168,9 @@ go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTV golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M= -golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -192,18 +178,13 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -220,31 +201,23 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/synkronus-cli/internal/cmd/qr.go b/synkronus-cli/internal/cmd/qr.go index 9437ac8f9..848208bd0 100644 --- a/synkronus-cli/internal/cmd/qr.go +++ b/synkronus-cli/internal/cmd/qr.go @@ -13,7 +13,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" qrcode "github.com/yeqown/go-qrcode/v2" - "github.com/yeqown/go-qrcode/writer/standard" ) // encodeFRMLS replicates the FRMLS encoding used by the Formulus generateQR.ts script. @@ -152,14 +151,19 @@ for the Formulus mobile app. The QR encodes server URL, username, and password.` outputFile = deriveOutputFilename(serverURL) } - // Generate QR code with logo using yeqown/go-qrcode + // Generate QR code with logo using yeqown/go-qrcode and custom stdlib PNG writer logoPath := "qr_logo.png" qrc, err := qrcode.New(encoded) if err != nil { return fmt.Errorf("failed to create QR code: %w", err) } - w, err := standard.New(outputFile, standard.WithLogoImageFilePNG(logoPath)) + logo, err := loadQRLogo(logoPath) + if err != nil { + return fmt.Errorf("failed to load QR logo: %w", err) + } + + w, err := newPNGWriter(outputFile, logo) if err != nil { return fmt.Errorf("failed to create QR writer: %w", err) } diff --git a/synkronus-cli/internal/cmd/qr_logo.png b/synkronus-cli/internal/cmd/qr_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a5994bc282bef2e9bc4597c28497501e27784345 GIT binary patch literal 35877 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 0 && logoH > 0 && logoW <= imgW && logoH <= imgH { + x0 := (imgW - logoW) / 2 + y0 := (imgH - logoH) / 2 + rect := image.Rect(x0, y0, x0+logoW, y0+logoH) + draw.Draw(img, rect, logo, bounds.Min, draw.Over) + } + } + + return img +} + +// loadQRLogo attempts to load the logo from the specified path, falling back to embedded default. +func loadQRLogo(logoPath string) (image.Image, error) { + if logoPath != "" { + if f, err := os.Open(logoPath); err == nil { + defer f.Close() + if img, err := png.Decode(f); err == nil { + return img, nil + } + } + } + + if len(defaultQRLogoBytes) > 0 { + img, err := png.Decode(bytes.NewReader(defaultQRLogoBytes)) + if err != nil { + return nil, fmt.Errorf("failed to decode embedded logo: %w", err) + } + return img, nil + } + + return nil, nil +}