packages feed

updo (empty) → 1.0.0

raw patch · 53 files changed

+2728/−0 lines, 53 filesdep +aesondep +basedep +dhall

Dependencies added: aeson, base, dhall, filepath, text, turtle, utf8-string

Files

+ ALTERNATIVES.md view
@@ -0,0 +1,76 @@+# Alternatives to Updo++Updo uses `.dhall` configuration but you may want to configure your project in+stack and mirror it to cabal or vica-versa with a project-to-project conversion.++Updo can be of help with mirroring if you chose stack-first or cabal-first+project maintenance but then you're own your own as far as managing GHC upgrades+goes.++## Project to Project Conversion++We provide makefiles for converting a stack project to a cabal project with+[`stack2cabal`](https://github.com/hasufell/stack2cabal) and for going the other+way with [`cabal2stack`](https://github.com/iconnect/cabal2stack).++### Configure with Stack++A stack project is YAML, `stack.yaml` by default. Without conditionals or+imports everything has to be in the same file except for stack's own [snapshot+indirection](https://docs.haskellstack.org/en/stable/custom_snapshot/) to+another file.++There are a bunch of YAML formatting tools such as+[vscode-yaml](https://www.npmjs.com/package/vscode-yaml).++### Configure with Cabal++A cabal project, `cabal.project` by default, is its own format. It has+conditionals and imports.++There is not a tool for formatting it. The `cabal format` command and the+[cabal-fmt](https://github.com/phadej/cabal-fmt) tool both format packages, not+projects.++### Mirroring with `stack2cabal`++This tool inspects the stack project and downloads source repositories to check+for version constraints and generates a `cabal.project` and+`cabal.project.freeze`.++This tool is slow as it clones whole and sometimes large source repositories+locally. This can be skipped with `--no-inspect-remotes` but the inspection+ensures correct versions in the freeze file.++```+> stack2cabal --help+Usage: stack2cabal [-f|--file STACK_YAML] [-o|--output-file CABAL_PROJECT] +                   [--no-inspect-remotes] [--no-pin-ghc] [--no-sort-repos] +                   [--no-run-hpack] [-p|--pin-hackage-index FUZZY_DATE]++Available options:+  --no-inspect-remotes     Don't check package names from remote git sources+                           (this is faster, but may leave incorrect versions in+                           cabal.project.freeze if remote packages overwrite+                           stack resolver versions)+```++It generates a freeze file but these locks are problematic as they don't+function as people expect and there are platform-specific [freeze+problems](https://github.com/haskell/cabal/issues/8059).++### Mirroring with `cabal2stack`++This tool inspects cabal's plan so is more tied to the machine architecture+where the plan was created by cabal. The generated stack project will differ if+created on mac or linux but by adding a `--resolver` flag the generated project+is buildable on both mac and linux.++Like `stack2cabal`, this tool also needs to download remote source repositories+and is about as slow because the download takes most of the time.++The generated stack project will [vary by platform][c2s1] and it will [repeat+dependencies][c2s4] already in the resolver.++[c2s1]: https://github.com/iconnect/cabal2stack/issues/1+[c2s4]: https://github.com/iconnect/cabal2stack/issues/4
+ CHANGELOG.md view
@@ -0,0 +1,2 @@+### 1.0.0+* First release
+ HACKING.md view
@@ -0,0 +1,70 @@+# Updo Hacking++## Package Groups++During the cabal solving step, cabal would fail to solve for dependencies for+packages in a particular order. The grouping of packages preserves this.++To mirror package groups from `project-dhall/pkgs/*.dhall` to+`project-cabal/pkgs/*.config` use make, either for `pkgs.config` that imports+all `pkgs/*.config` or for a single package group.++```+$ make project-cabal/pkgs.config+$ cat project-cabal/pkgs.config+import: project-cabal/pkgs/shims.config+...+import: project-cabal/pkgs/tools.config+```++When `project-dhall/pkgs-upgrade-todo.dhall` is:++```dhall+[ "./jobs/runner/" ]+```++Then the single `jobs.config` package group as generated is:++```+$ make project-cabal/pkgs/jobs.config+$ cat project-cabal/pkgs/jobs.config+if impl(ghc <= 8.10.7)+  packages:+      ./jobs/core/+    , ./jobs/runner/+else+  packages:+      ./jobs/core/++-- TODO for impl(ghc >= 9.2.5)+-- ./jobs/runner/+```++To split a group of packages into a dhall record of `{pkgs, done, todo}`:++```+$ ./updo/project-dhall/pkgs-upgrade-partition.hs ./project-dhall/pkgs/jobs.dhall+{ pkgs = [ "./jobs/core/", "./jobs/runner/" ]+, done = [ "./jobs/core/" ]+, todo = [ "./jobs/runner/" ]+}+```++## GHC Upgrade Hacking++During a GHC version uprade, start by putting all packages into+`pkgs-upgrade-todo.dhall` and progressively take one or a few packages out at at+time and rerun the make targets to build the project you want to use.++To sort all the packages.++```+$ cd project-dhall+$ ./pkgs-sorted.hs+[ "./admin-client/"+, "./aeson-shim/"+...+, "./docs/"+, "./testing/"+]+```
+ LICENSE view
@@ -0,0 +1,373 @@+Mozilla Public License Version 2.0+==================================++1. Definitions+--------------++1.1. "Contributor"+    means each individual or legal entity that creates, contributes to+    the creation of, or owns Covered Software.++1.2. "Contributor Version"+    means the combination of the Contributions of others (if any) used+    by a Contributor and that particular Contributor's Contribution.++1.3. "Contribution"+    means Covered Software of a particular Contributor.++1.4. "Covered Software"+    means Source Code Form to which the initial Contributor has attached+    the notice in Exhibit A, the Executable Form of such Source Code+    Form, and Modifications of such Source Code Form, in each case+    including portions thereof.++1.5. "Incompatible With Secondary Licenses"+    means++    (a) that the initial Contributor has attached the notice described+        in Exhibit B to the Covered Software; or++    (b) that the Covered Software was made available under the terms of+        version 1.1 or earlier of the License, but not also under the+        terms of a Secondary License.++1.6. "Executable Form"+    means any form of the work other than Source Code Form.++1.7. "Larger Work"+    means a work that combines Covered Software with other material, in+    a separate file or files, that is not Covered Software.++1.8. "License"+    means this document.++1.9. "Licensable"+    means having the right to grant, to the maximum extent possible,+    whether at the time of the initial grant or subsequently, any and+    all of the rights conveyed by this License.++1.10. "Modifications"+    means any of the following:++    (a) any file in Source Code Form that results from an addition to,+        deletion from, or modification of the contents of Covered+        Software; or++    (b) any new file in Source Code Form that contains any Covered+        Software.++1.11. "Patent Claims" of a Contributor+    means any patent claim(s), including without limitation, method,+    process, and apparatus claims, in any patent Licensable by such+    Contributor that would be infringed, but for the grant of the+    License, by the making, using, selling, offering for sale, having+    made, import, or transfer of either its Contributions or its+    Contributor Version.++1.12. "Secondary License"+    means either the GNU General Public License, Version 2.0, the GNU+    Lesser General Public License, Version 2.1, the GNU Affero General+    Public License, Version 3.0, or any later versions of those+    licenses.++1.13. "Source Code Form"+    means the form of the work preferred for making modifications.++1.14. "You" (or "Your")+    means an individual or a legal entity exercising rights under this+    License. For legal entities, "You" includes any entity that+    controls, is controlled by, or is under common control with You. For+    purposes of this definition, "control" means (a) the power, direct+    or indirect, to cause the direction or management of such entity,+    whether by contract or otherwise, or (b) ownership of more than+    fifty percent (50%) of the outstanding shares or beneficial+    ownership of such entity.++2. License Grants and Conditions+--------------------------------++2.1. Grants++Each Contributor hereby grants You a world-wide, royalty-free,+non-exclusive license:++(a) under intellectual property rights (other than patent or trademark)+    Licensable by such Contributor to use, reproduce, make available,+    modify, display, perform, distribute, and otherwise exploit its+    Contributions, either on an unmodified basis, with Modifications, or+    as part of a Larger Work; and++(b) under Patent Claims of such Contributor to make, use, sell, offer+    for sale, have made, import, and otherwise transfer either its+    Contributions or its Contributor Version.++2.2. Effective Date++The licenses granted in Section 2.1 with respect to any Contribution+become effective for each Contribution on the date the Contributor first+distributes such Contribution.++2.3. Limitations on Grant Scope++The licenses granted in this Section 2 are the only rights granted under+this License. No additional rights or licenses will be implied from the+distribution or licensing of Covered Software under this License.+Notwithstanding Section 2.1(b) above, no patent license is granted by a+Contributor:++(a) for any code that a Contributor has removed from Covered Software;+    or++(b) for infringements caused by: (i) Your and any other third party's+    modifications of Covered Software, or (ii) the combination of its+    Contributions with other software (except as part of its Contributor+    Version); or++(c) under Patent Claims infringed by Covered Software in the absence of+    its Contributions.++This License does not grant any rights in the trademarks, service marks,+or logos of any Contributor (except as may be necessary to comply with+the notice requirements in Section 3.4).++2.4. Subsequent Licenses++No Contributor makes additional grants as a result of Your choice to+distribute the Covered Software under a subsequent version of this+License (see Section 10.2) or under the terms of a Secondary License (if+permitted under the terms of Section 3.3).++2.5. Representation++Each Contributor represents that the Contributor believes its+Contributions are its original creation(s) or it has sufficient rights+to grant the rights to its Contributions conveyed by this License.++2.6. Fair Use++This License is not intended to limit any rights You have under+applicable copyright doctrines of fair use, fair dealing, or other+equivalents.++2.7. Conditions++Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted+in Section 2.1.++3. Responsibilities+-------------------++3.1. Distribution of Source Form++All distribution of Covered Software in Source Code Form, including any+Modifications that You create or to which You contribute, must be under+the terms of this License. You must inform recipients that the Source+Code Form of the Covered Software is governed by the terms of this+License, and how they can obtain a copy of this License. You may not+attempt to alter or restrict the recipients' rights in the Source Code+Form.++3.2. Distribution of Executable Form++If You distribute Covered Software in Executable Form then:++(a) such Covered Software must also be made available in Source Code+    Form, as described in Section 3.1, and You must inform recipients of+    the Executable Form how they can obtain a copy of such Source Code+    Form by reasonable means in a timely manner, at a charge no more+    than the cost of distribution to the recipient; and++(b) You may distribute such Executable Form under the terms of this+    License, or sublicense it under different terms, provided that the+    license for the Executable Form does not attempt to limit or alter+    the recipients' rights in the Source Code Form under this License.++3.3. Distribution of a Larger Work++You may create and distribute a Larger Work under terms of Your choice,+provided that You also comply with the requirements of this License for+the Covered Software. If the Larger Work is a combination of Covered+Software with a work governed by one or more Secondary Licenses, and the+Covered Software is not Incompatible With Secondary Licenses, this+License permits You to additionally distribute such Covered Software+under the terms of such Secondary License(s), so that the recipient of+the Larger Work may, at their option, further distribute the Covered+Software under the terms of either this License or such Secondary+License(s).++3.4. Notices++You may not remove or alter the substance of any license notices+(including copyright notices, patent notices, disclaimers of warranty,+or limitations of liability) contained within the Source Code Form of+the Covered Software, except that You may alter any license notices to+the extent required to remedy known factual inaccuracies.++3.5. Application of Additional Terms++You may choose to offer, and to charge a fee for, warranty, support,+indemnity or liability obligations to one or more recipients of Covered+Software. However, You may do so only on Your own behalf, and not on+behalf of any Contributor. You must make it absolutely clear that any+such warranty, support, indemnity, or liability obligation is offered by+You alone, and You hereby agree to indemnify every Contributor for any+liability incurred by such Contributor as a result of warranty, support,+indemnity or liability terms You offer. You may include additional+disclaimers of warranty and limitations of liability specific to any+jurisdiction.++4. Inability to Comply Due to Statute or Regulation+---------------------------------------------------++If it is impossible for You to comply with any of the terms of this+License with respect to some or all of the Covered Software due to+statute, judicial order, or regulation then You must: (a) comply with+the terms of this License to the maximum extent possible; and (b)+describe the limitations and the code they affect. Such description must+be placed in a text file included with all distributions of the Covered+Software under this License. Except to the extent prohibited by statute+or regulation, such description must be sufficiently detailed for a+recipient of ordinary skill to be able to understand it.++5. Termination+--------------++5.1. The rights granted under this License will terminate automatically+if You fail to comply with any of its terms. However, if You become+compliant, then the rights granted under this License from a particular+Contributor are reinstated (a) provisionally, unless and until such+Contributor explicitly and finally terminates Your grants, and (b) on an+ongoing basis, if such Contributor fails to notify You of the+non-compliance by some reasonable means prior to 60 days after You have+come back into compliance. Moreover, Your grants from a particular+Contributor are reinstated on an ongoing basis if such Contributor+notifies You of the non-compliance by some reasonable means, this is the+first time You have received notice of non-compliance with this License+from such Contributor, and You become compliant prior to 30 days after+Your receipt of the notice.++5.2. If You initiate litigation against any entity by asserting a patent+infringement claim (excluding declaratory judgment actions,+counter-claims, and cross-claims) alleging that a Contributor Version+directly or indirectly infringes any patent, then the rights granted to+You by any and all Contributors for the Covered Software under Section+2.1 of this License shall terminate.++5.3. In the event of termination under Sections 5.1 or 5.2 above, all+end user license agreements (excluding distributors and resellers) which+have been validly granted by You or Your distributors under this License+prior to termination shall survive termination.++************************************************************************+*                                                                      *+*  6. Disclaimer of Warranty                                           *+*  -------------------------                                           *+*                                                                      *+*  Covered Software is provided under this License on an "as is"       *+*  basis, without warranty of any kind, either expressed, implied, or  *+*  statutory, including, without limitation, warranties that the       *+*  Covered Software is free of defects, merchantable, fit for a        *+*  particular purpose or non-infringing. The entire risk as to the     *+*  quality and performance of the Covered Software is with You.        *+*  Should any Covered Software prove defective in any respect, You     *+*  (not any Contributor) assume the cost of any necessary servicing,   *+*  repair, or correction. This disclaimer of warranty constitutes an   *+*  essential part of this License. No use of any Covered Software is   *+*  authorized under this License except under this disclaimer.         *+*                                                                      *+************************************************************************++************************************************************************+*                                                                      *+*  7. Limitation of Liability                                          *+*  --------------------------                                          *+*                                                                      *+*  Under no circumstances and under no legal theory, whether tort      *+*  (including negligence), contract, or otherwise, shall any           *+*  Contributor, or anyone who distributes Covered Software as          *+*  permitted above, be liable to You for any direct, indirect,         *+*  special, incidental, or consequential damages of any character      *+*  including, without limitation, damages for lost profits, loss of    *+*  goodwill, work stoppage, computer failure or malfunction, or any    *+*  and all other commercial damages or losses, even if such party      *+*  shall have been informed of the possibility of such damages. This   *+*  limitation of liability shall not apply to liability for death or   *+*  personal injury resulting from such party's negligence to the       *+*  extent applicable law prohibits such limitation. Some               *+*  jurisdictions do not allow the exclusion or limitation of           *+*  incidental or consequential damages, so this exclusion and          *+*  limitation may not apply to You.                                    *+*                                                                      *+************************************************************************++8. Litigation+-------------++Any litigation relating to this License may be brought only in the+courts of a jurisdiction where the defendant maintains its principal+place of business and such litigation shall be governed by laws of that+jurisdiction, without reference to its conflict-of-law provisions.+Nothing in this Section shall prevent a party's ability to bring+cross-claims or counter-claims.++9. Miscellaneous+----------------++This License represents the complete agreement concerning the subject+matter hereof. If any provision of this License is held to be+unenforceable, such provision shall be reformed only to the extent+necessary to make it enforceable. Any law or regulation which provides+that the language of a contract shall be construed against the drafter+shall not be used to construe this License against a Contributor.++10. Versions of the License+---------------------------++10.1. New Versions++Mozilla Foundation is the license steward. Except as provided in Section+10.3, no one other than the license steward has the right to modify or+publish new versions of this License. Each version will be given a+distinguishing version number.++10.2. Effect of New Versions++You may distribute the Covered Software under the terms of the version+of the License under which You originally received the Covered Software,+or under the terms of any subsequent version published by the license+steward.++10.3. Modified Versions++If you create software not governed by this License, and you want to+create a new license for such software, you may create and use a+modified version of this License if you rename the license and remove+any references to the name of the license steward (except to note that+such modified license differs from this License).++10.4. Distributing Source Code Form that is Incompatible With Secondary+Licenses++If You choose to distribute Source Code Form that is Incompatible With+Secondary Licenses under the terms of this version of the License, the+notice described in Exhibit B of this License must be attached.++Exhibit A - Source Code Form License Notice+-------------------------------------------++  This Source Code Form is subject to the terms of the Mozilla Public+  License, v. 2.0. If a copy of the MPL was not distributed with this+  file, You can obtain one at http://mozilla.org/MPL/2.0/.++If it is not possible or desirable to put the notice in a particular+file, then You may include the notice in a location (such as a LICENSE+file in a relevant directory) where a recipient would be likely to look+for such a notice.++You may add additional accurate notices of copyright ownership.++Exhibit B - "Incompatible With Secondary Licenses" Notice+---------------------------------------------------------++  This Source Code Form is "Incompatible With Secondary Licenses", as+  defined by the Mozilla Public License, v. 2.0.
+ Makefile view
@@ -0,0 +1,103 @@+# How are we going to generate stack.yaml and stack.yaml.lock files?+#+# Choices are:+#  - dhall2stack = via a temporary ghc-x.y.z.dhall2stack.yaml+#  - dhall2yaml2stack = via a temporary ghc-x.y.z.dhall2yaml2stack.yaml+STACK_VIA := dhall2stack++# How are we going to generate the cabal.project file?+#+# Choices are:+#  - dhall2config = via a temporary ghc-x.y.z.dhall2config.project+#  - dhall2cabal = via a temporary ghc-x.y.z.dhall2cabal.project+#+# dhall2config is good when upgrading to a new GHC version. The+# <pkg-group>.config files will show how many in a group are yet to be upgraded.+# With the cabal imports it is possible to try things out quickly by editing the+# config files themselves if you want to momentarily sidestep Updo's make cabal+# project generation.+#+# dhall2cabal is good if you want the cabal project to closely match the layout+# of the stack project when that is generated using dhall2stack.++# dhall2cabal is also good if you need to use cabal < 3.8 where everything needs+# to be in one file. To achieve this, the+# project-dhall/project-dhall2cabal.dhall template will need to bring in the+# contents of the stackage import like this:+#+# ${../../project-stackage/$(STACKAGE_VERSION).config as Text}+CABAL_VIA := dhall2config++UPDO_TMP := .updo++# Updo Dhall gives us these targets:+#  - dhall2stack-projects+#  - dhall2cabal-projects+#  - dhall2config-projects+#  - project-sha256maps+include updo/project-dhall/Makefile+include updo/project-dhall2config/Makefile+include updo/project-nix/Makefile++# Project (and related sha256map) files used in production, not in GHC upgrade.+.PHONY: projects+projects: \+  ghc-$(GHC_VERSION).sha256map.nix \+  stack.yaml \+  stack.yaml.lock \+  cabal.project++.PHONY: upgrade-projects+upgrade-projects: \+  project-nix/ghc-$(GHC_UPGRADE)/sha256map.nix \+  stack.upgrade.yaml \+  stack.upgrade.yaml.lock \+  cabal.upgrade.project \+  ghc-$(GHC_UPGRADE).dhall2stack.yaml \+  ghc-$(GHC_UPGRADE).dhall2stack.yaml.lock \+  ghc-$(GHC_UPGRADE).dhall2cabal.project++# All the kinds of project files we might want to generate.+#+# These are alternative methods we could include but don't.+#  - dhall2yaml2stack-projects+.PHONY: all-possible-projects+all-possible-projects: \+  projects \+  dhall2config-projects \+  dhall2cabal-projects \+  dhall2stack-projects++.PHONY: clean+clean:+	rm -f ghc-*.stack.* ghc-*.dhall2config.* ghc-*.dhall2cabal.* ghc-*.dhall2stack.* ghc-*.stack2cabal.* ghc-*.cabal2stack.* ghc-*.dhall2yaml2stack.* ghc-*.sha256map.nix++.PHONY: cabal+cabal: \+  ghc-$(GHC_VERSION).$(CABAL_VIA).project \+  ghc-$(GHC_UPGRADE).$(CABAL_VIA).project++.PHONY: stack+stack: \+  ghc-$(GHC_VERSION).$(STACK_VIA).yaml \+  ghc-$(GHC_VERSION).$(STACK_VIA).yaml.lock \+  ghc-$(GHC_UPGRADE).$(STACK_VIA).yaml \+  ghc-$(GHC_UPGRADE).$(STACK_VIA).yaml.lock++cabal.project: ghc-$(GHC_VERSION).$(CABAL_VIA).project+	cp $^ $@++cabal.upgrade.project: ghc-$(GHC_UPGRADE).$(CABAL_VIA).project+	cp $^ $@++stack.yaml: ghc-$(GHC_VERSION).$(STACK_VIA).yaml+	cp $< $@++stack.upgrade.yaml: ghc-$(GHC_UPGRADE).$(STACK_VIA).yaml+	cp $< $@++stack.yaml.lock: stack.yaml+	stack build --test --no-run-tests --bench --no-run-benchmarks --dry-run --stack-yaml $<++stack.upgrade.yaml.lock: stack.upgrade.yaml+	stack build --test --no-run-tests --bench --no-run-benchmarks --dry-run --stack-yaml $<
+ README.md view
@@ -0,0 +1,567 @@+# 🫸 Updo 🫷++> A hairstyle and a way of maintaining and upgrading Haskell projects.++### What is a Project?++Updo is good for project packages, constraints, source repository packages and+stackage resolver[^download-stackage-config]. More than this can be added into a+project template.++[^download-stackage-config]: For cabal, the cabal.config will have to be+  downloaded to `project-stackage/resolver.config` and then edited to comment+  out constraints that would otherwise lead to impossible to solve conflicting+  constraints because cabal constraints are additive and cannot be overridden.++### Goals++1. Map a single set of packages, constraints and source repository packages to+   projects for cabal and stack.+2. Minimize the effort of upgrading constraints, source repository packages and+   GHC versions.++We do this with configuration in `.dhall` files, makefiles and `dhall text`+templates. We also have some simple Haskell scripts for operations not possible+in Dhall like sorting a `List Text`[^dhall-sort-list-nat].++[^dhall-sort-list-nat]: Dhall can only sort `List Natural`.++# Template Outputs++We generate projects from the configured inputs using templates.  With the+`dhall text` command we're able to mirror to both stack and cabal projects using+text templates. See [templates](./TEMPLATES.md) for more.++We provide one stack template `dhall2stack`. The `dhall2cabal` template for+cabal is so similar that the generated outputs can be compared using file diff+tooling. However, we prefer the `dhall2config` template because it retains+package groups that can bring order to an [upgrade](#upgrading-a-project) if the+cabal solving order is roughly captured by package group ordering.++We provide one level of nesting for grouping packages together as inputs. Groups+are preserved or flattened depending on the template.++* `dhall2stack` - Stack projects cannot import so necessarily must lose the+  grouping and contain the list of packages.+* `dhall2cabal` - This template lists packages within the cabal project.+* `dhall2config` - When generating the cabal project we keep [package+  groups](#package-groups), generating the same tree structure but replacing the+  leaves. From each `group-name.dhall` we generate a `group-name.config` file.++It is nice to be able to look at packages both grouped and as one sorted list.+Find the flat sorted lists of packages used by some templates in the `.updo`+folder.++```+.updo+├── pkgs-sorted.dhall        ▨ List Text+└── pkgs-upgrade-done.dhall  ▨ List Text+```++If you don't want groups then please put all packages in the one group.++# Maintaining a Project++Maintaining a project involves configuring or reconfiguring inputs and then+using make to generate the projects. What do we want to configure? We don't need+much:++1. A stackage resolver[^download-stackage-config].+2. A list of constraints (package dependencies with their versions).+3. A list of source repository packages, for unpublished[^unpublished-packages]+  packages or for unpublished versions of packages.+4. A list of packages.++[^unpublished-packages]: A package not published to hackage or stackage.+++All configuration goes into `./project-dhall` (where `.` is the root folder for+your Haskell project) except for the `cabal.config` that we'll need to download+from stackage. Save this file as `project-stackage/lts-m.n.config` using the+exact resolver name.++```+project-stackage+└── lts-m.n.config+```++For each `ghc-x.y.z` compiler version, create this set of inputs and templates:++```+project-dhall+└── ghc-x.y.z+    ├── constraints.dhall      ▨ List { dep : Text, ver : Text }+    ├── deps-external.dhall    ▨ List { loc : Text, tag : Text, sub : List Text }+    ├── deps-internal.dhall    ▨ List { loc : Text, tag : Text, sub : List Text }+    ├── forks-external.dhall   ▨ List { loc : Text, tag : Text, sub : List Text }+    ├── forks-internal.dhall   ▨ List { loc : Text, tag : Text, sub : List Text }+    └── text-templates+        ├── dhall2cabal.dhall  ▨ template for ghc-x.y.z.dhall2cabal.project+        ├── dhall2config.dhall ▨ template for ghc-x.y.z.dhall2config.project+        ├── dhall2stack.dhall  ▨ template for ghc-x.y.z.dhall2stack.yaml+        └── stacksnippet.dhall ▨ anything for ghc-x.y.z.dhall2stack.yaml+```++Anything in `stacksnippet.dhall` gets added to the top of the generated stack+project[^base.yaml].  The rest of the files are inputs.++[^base.yaml]: Anything in `stacksnippet.dhall` will be used by+  [dhall2yaml2stack](alternatives/yaml2stack#readme) too and is put into a+  `base.yaml`, the topmost fragment when stitching together fragments.++### 1. Stackage Resolver and Constraints++The stackage resolver is able to provide us with a list of packages that work+together for both stack and cabal. Any other dependencies as constraints or+source packages are either not found on stackage or are different versions.++By specifying a resolver with stack we get a list of exact `==` versions for+packages published to stackage. Stackage also publishes a `cabal.config` file+that has these package versions as cabal constraints. As cabal constraints are+additive, in practice importing directly from stackage often leads to conflicts+between stackage's constraints and the project's own constraints and for that+reason we'll usually have to download the `cabal.config` from+stackage[^stackage-config-url-path] and comment out lines that are conflicted.++[^stackage-config-url-path]: The URL path to download this file from is+  `/resolver/cabal.config`. The latest nightly is always at+  https://www.stackage.org/nightly/cabal.config.++We don't expect any packages to impose constraints on their dependencies at the+package level, in their `.cabal` files, but if they do then these constraints+must fit with constraints at the project level.++### 2. Constraints++In constraints[^constraints] put published packages that you want to use that are+not on stackage or if they are on stackage where you want to use a different+version.++[^constraints]: To stack, constraints and source repository packages are+    both `extra-deps`. We use cabal-like nomenclature but, be warned, cabal+    constraints cover more than just `package ==version`, such as flags:+    ```cabal+    constraints:+        bson -_old-network+      , HaXml +splitbase+    ```++```dhall+[ { dep = "diagrams-postscript", ver = "1.5" }+, { dep = "diagrams-svg", ver = "1.4.3" }+]+```++The type of constraints is a list of records with dependency and version fields:++```dhall+List { dep : Text, ver : Text }+```++### 3. Source Repository Packages++There are various reasons to depend on source packages and forks of source+packages; to keep a mirror within your organization, to add fixes that haven't been+upstreamed yet, or to pick up a fix someone else has made but hasn't yet been merged+upstream, released and published.++```+project-dhall+└── ghc-x.y.z+    ├── deps-external.dhall    ▨ List { loc : Text, tag : Text, sub : List Text }+    ├── deps-internal.dhall    ▨ List { loc : Text, tag : Text, sub : List Text }+    ├── forks-external.dhall   ▨ List { loc : Text, tag : Text, sub : List Text }+    └── forks-internal.dhall   ▨ List { loc : Text, tag : Text, sub : List Text }+```++The type of these files are all the same. They contain a list of records with+fields for location, tag (commit SHA) and subdirectories:++```dhall+List { loc : Text, tag : Text, sub : List Text }+```++Any empty `sub` field must be type annotated like this `[] : List Text`. Each+source repository package record will bring in one dependency or as many as+there are `sub` fields.++These source repository `*.dhall` files are for original work from your+organisation (internal) and from third parties (external) and for internal and+external forks. If using the empty list in any one of these files then use an+explicit type annotation:++```dhall+[] : List { loc : Text, tag : Text, sub : List Text }+```++### 4. Package Groups+In the `pkgs` folder, create one or more groups for related packages.++```+project-dhall+├── ghc-x.y.z+├── pkgs+│   ├── db.dhall      ▨ List Text+│   ├── server.dhall  ▨ List Text+│   └── tools.dhall   ▨ List Text+└── pkg-groups.dhall  ▨ List Text+```++The contents of each group is a `List Text` of relative paths to folders+containing package `.cabal` files.++```dhall+-- ./project-dhall/pkgs/tools.dhall+[ "tool/linter/"+, "tool/formatter/"+]+```++List the package groups in `pkg-groups.dhall`[^pkg-groups]. A good order to use+is the order of cabal constraint solving that you may witness when upgrading a+project and seeing solving failures.++[^pkg-groups]: Dhall can't do arbitrary IO like reading files from a folder.++```dhall+-- ./project-dhall/pkg-groups.dhall+[ "tools"+, "db"+, "server"+]+```++This gives you control of the imports in the generated+`./project-cabal/pkgs.config`, itself imported into+`ghc-x.y.z.dhall2config.project`:++* Use `CABAL_RELATIVITY=ImportRelative` to be import-compatible with `cabal-3.10`.+  ```cabal+  -- ./project-cabal/pkgs.config+  import: ./pkgs/tools.config+  import: ./pkgs/db.config+  import: ./pkgs/server.config+  ```+* The default of `CABAL_RELATIVITY=ProjectRelative` is import-compatible with `cabal-3.8`.+  ```cabal+  -- ./project-cabal/pkgs.config+  import: ./project-cabal/pkgs/tools.config+  import: ./project-cabal/pkgs/db.config+  import: ./project-cabal/pkgs/server.config+  ```+++```cabal+-- ./ghc-x.y.z.dhall2config.project+import: ./project-stackage/lts-m.n.config++import: ./project-cabal/pkgs.config++import: ./project-cabal/ghc-x.y.z/constraints.config+import: ./project-cabal/ghc-x.y.z/deps-external.config+import: ./project-cabal/ghc-x.y.z/deps-internal.config+import: ./project-cabal/ghc-x.y.z/forks-external.config+import: ./project-cabal/ghc-x.y.z/forks-internal.config+```++# Upgrading a Project++### GHC Upgrade+For a GHC compiler upgrade, add another folder to `project-dhall` for the+upgrade from `ghc-u.v.w` to `ghc-x.y.z`:++```+project-dhall+├── ghc-u.v.w+├── ghc-x.y.z+├── pkgs+├── pkg-groups.dhall        ▨ List Text+└── pkgs-upgrade-todo.dhall ▨ List Text+```++At the start of a GHC upgrade put all packages into+`project-dhall/pkgs-upgrade-todo.dhall`. As the upgrade progresses remove+packages from this list as you work on them.++### Constraint Upgrade+For a dependency version upgrade, add or change an entry in `constraints.dhall`+and if that dependency is on stackage for the resolver we're using and if the+version there differs then comment out that entry in the downloaded+`project-stackage/resolver-name.config` file to avoid impossible constraint+solving conflicts with cabal.++### Source Repository Upgrade+For a source repository upgrade, bump the `tag` field if picking up a newer+version.++If the `tag` you were on has been superseded by a published version then delete+the entry and add a constraint unless the published version is already a match+in the downloaded `cabal.config` file from stackage.++### Adding a Fork+If you are forking then you'll want to add a record to `fork-internal.dhall` and+then:+- If forking a stackage package: comment out the entry in the downloaded `cabal.config`+- If forking a hackage package: delete the matching entry in `constraints.dhall`++Using someone else's fork is the same except add the record to+`fork-external.dhall`.++Unforking goes the other way, remove the `fork-*.dhall` entry and either+fallback to the stackage version or another version for which you'll add an+entry in `constraints.dhall`.++# Getting Started++Dhall is a prerequisite.++As well as other make targets, updo can bootstrap itself by adding one of two+recipes to `project-files.mk`:++```make+UPDO_VERSION ?= 1.0.0+HACKAGE := http://hackage.haskell.org/package+UPDO_URL := ${HACKAGE}/updo-${UPDO_VERSION}/updo-${UPDO_VERSION}.tar.gz+```++1. When opting for the scripts:++```make+updo/Makefile:+	rm -rf updo+	curl -sSL ${UPDO_URL} | tar -xz+	mv updo-* updo+	chmod +x $$(grep -RIl '^#!' updo)+```++2. When opting to install the included executables:++```make+updo/Makefile:+	rm -rf updo+	curl -sSL ${UPDO_URL} | tar -xz+	cd updo-${UPDO_VERSION}+	stack install+	cd -+	mv updo-* updo+```++Instead of curl and tar, `cabal get` or `stack unpack` can unpack package sources.++# Make Targets++In the root of your project, add two files.++```+.+├── project-files.mk+└── project-versions.mk+```++In `project-versions.mk` set variables for GHC and STACKAGE.++```make+GHC_VERSION := u.v.w+GHC_UPGRADE := x.y.z+STACKAGE_VERSION := lts-j.k+STACKAGE_UPGRADE := lts-m.n+```++Here is a basic set up for `project-files.mk`:++```make+include project-versions.mk+include updo/Makefile+```++### Canonical Targets++We consider the following as canonical project names and no target is required+to build them (they're built by the default `all` target):++```+$ make -f project-files.mk+```++- `stack.yaml`+- `cabal.project`+- `stack.upgrade.yaml`+- `cabal.upgrade.project`++We use CABAL_VIA and STACK_VIA makefile variables to decide which `ghc-x.y.z`+prefixed projects will be copied to the default project names (`cabal.project`+and `stack.yaml`). This way, Updo will create a single pair of projects for one+version of GHC.++### GHC-Prefixed Targets++We can build `ghc-x.y.z` projects explicitly:++```+$ make -f project-files.mk ghc-x.y.z.dhall2config.project+$ make -f project-files.mk ghc-x.y.z.dhall2cabal.project+$ make -f project-files.mk ghc-x.y.z.dhall2stack.yaml+```++### Modified Time Travel++Be careful with modified time that make relies on. If you hand-modify a target+so that it is newer than a prerequisite then make will not use its recipe to+build the target. This is easy to do inadvertently with a git GUI tool when+discarding changes or by doing a `git restore <project-name>`. As git advances+the modified time, this is effectively the same as `touch <project-name>`.  To+recover from this situation, either;+- Remove the target with `rm <project-name>`.+- Run make with its `--always-make` flag but be careful with this if+  bootstrapping updo itself within the makefile.++### Lock Files++For stack, the project and its lock are separate targets (`.yaml` and+`.yaml.lock`).++```+$ make -f project-files.mk ghc-x.y.z.dhall2stack.yaml+dhall text --file project-dhall/ghc-x.y.z/project-stack.dhall > ghc-x.y.z.dhall2stack.yaml+```++```+$ make -f project-files.mk ghc-x.y.z.dhall2stack.yaml.lock+stack build --dry-run --stack-yaml ghc-x.y.z.dhall2stack.yaml+```++We don't provide a make target for cabal `.project.freeze` files, relying+instead on a combination of constraints, those in the `cabal.config` downloaded+from stackage and those we add ourselves to `constraints.dhall`. If you do want+to freeze anyway there's a cabal command to generate a freeze file.++```+$ make -f project-files.mk ghc-x.y.z.dhall2cabal.project+dhall text --file project-dhall/ghc-x.y.z/project-cabal.dhall > ghc-x.y.z.dhall2cabal.project+```++# Clean GHC-Prefixed Projects++There's a make target to remove all `ghc-x.y.z` prefixed projects:++```+$ make -f project-files.mk clean+rm -f ghc-*.stack.* ghc-*.dhall2config.* ghc-*.dhall2cabal.* ghc-*.dhall2stack.*+ghc-*.stack2cabal.* ghc-*.cabal2stack.* ghc-*.dhall2yaml2stack.* ghc-*.sha256map.nix+```++# GHC-Prefixed Projects as Temporary++To treat `ghc-x.y.z` prefixed files as temporary, add lines like these to+`project-files.mk`:++```make+.INTERMEDIATE: ghc-$(GHC_VERSION).dhall2config.project+.INTERMEDIATE: ghc-$(GHC_VERSION).dhall2cabal.project+.INTERMEDIATE: ghc-$(GHC_VERSION).dhall2stack.yaml+.INTERMEDIATE: ghc-$(GHC_VERSION).sha256map.nix+```++# SHA256 Map Generation Method++With this snippet from `project-files.mk`, we can switch between two methods of+generating `ghc-x.y.z.sha256map.nix`.++```make+# How to generate project-nix/ghc-$(GHC_VERSION)/sha256map.nix?+# This is copied from ghc-$(GHC_VERSION).sha256map.nix.+#  - false to generate from *.dhall inputs via sha256map.hs.+#  - true to generate from stack.yaml via sha256map.py.+SHA256MAP_VIA_PYTHON ?= false++# If true, generate the sha256map from the stack.yaml with python,+# overriding the recipe for this target.+ifeq ($(SHA256MAP_VIA_PYTHON), true)+ghc-$(GHC_VERSION).sha256map.nix: stack.yaml+	updo/project-nix/sha256map.py <$^ >$@+endif+```++The default is to use the `sha256map.hs` script to generate+it[^replacing_versions]:++[^replacing_versions]: Using ghc-x.y.z and lts-m.n in the example output, not+  the actual GHC version or resolver.++```+$ make -f project-files.mk ghc-x.y.z.sha256map.nix+echo \+  '[./project-dhall/ghc-x.y.z/deps-external.dhall+  , ./project-dhall/ghc-x.y.z/deps-internal.dhall+  , ./project-dhall/ghc-x.y.z/forks-external.dhall+  , ./project-dhall/ghc-x.y.z/forks-internal.dhall+  , ([] : List {loc : Text, tag : Text, sub : List Text})+  ]' \+  | ./updo/project-nix/sha256map.hs > ghc-x.y.z.sha256map.nix+```++To use the `sha256map.py` script instead that is much slower:++```+$ SHA256MAP_VIA_PYTHON=true \+  make -f project-files.mk project-nix/ghc-x.y.z/sha256map.nix+updo/project-nix/sha256map.py <stack.yaml >ghc-x.y.z.sha256map.nix+mkdir -p project-nix/ghc-x.y.z && cp ghc-x.y.z.sha256map.nix project-nix/ghc-x.y.z/sha256map.nix+rm ghc-x.y.z.sha256map.nix++```++You can [read more about Updo Nix](project-nix#readme) and its use with+haskell.nix.++# Installed Exes or Scripts++To use the executables of Updo's own package, by default, instead of its+scripts, add these lines to `project-files.mk`:++```make+SHA256MAP_HS_EXE ?= true+PKG_GROUPS_HS_EXE ?= true+PKGS_SORTED_HS_EXE ?= true+PKGS_UPGRADE_DONE_HS_EXE ?= true+```++### Why Dhall for Configuration?++Dhall has excellent imports and is an intentionally limited typed programming+language. We use dhall's `text` command to write cabal and stack projects using+[dhall text templating][dhall-text-templating].++The `dhall` executable comes with a `format` command[^format-lsp]. This helps in+the maintenance of the various `.dhall` files.++An example of the limits of the language we encountered in Updo is not being+able to compare anything but `Natural` values. Sorting something like `List+Text` is not possible but there is great interop with Haskell so this can be+done over there as we do with scripts.++## Acknowledgements++I already knew Dhall well from maintaining hpack-dhall. Thanks Simon Hengel for+suggesting I take over its maintenance.++FP Complete is the corporate sponsor of this tooling. Thanks to my collegues;+Daniel Herrera Rendón for first seeing this could work, Neil Mayhew for makefile+tips and tricks, Abel Sen for careful reviews and thoughtful suggestions and+Eduardo Lemos Rocha for advocacy and enthusiasm.++Please contact me for any questions, problems, suggestions or the like.+Maintenance of Updo will be coming from me and not FP Complete.++Updo grew to fill a need after every other way I could find to maintain projects+was unsatisfactory for the specific problem I had, herding hundreds of packages+and their dependencies into buildable projects over two compiler versions for+both cabal and stack.  The more I use it, the more I love it as a fast, simple+and appropriate solution for maintaining Haskell projects.++Thanks FP Complete.++[^format-lsp]: Formatting is also available with the [Dhall LSP Server][LSP].++[dhall-text-templating]: https://www.haskellforall.com/2017/06/dhall-is-now-template-engine.html+[LSP]: https://github.com/PanAeon/vscode-dhall-lsp-server
+ TEMPLATES.md view
@@ -0,0 +1,21 @@+# Dhall Text Templates++We use Dhall text templates in `project-dhall/ghc-x.y.z/text-templates`:++- From template `dhall2cabal.dhall`, we generate `ghc-x.y.z.dhall2cabal.project`+- From template `dhall2stack.dhall`, we generate `ghc-x.y.z.dhall2stack.yaml`++Dhall text templating was+[announced](https://www.haskellforall.com/2017/06/dhall-is-now-template-engine.html)+in June 2017 and this is now a command of the dhall executable:++```+$ dhall text --help`+Usage: dhall text [--file FILE] [--output FILE]+  Render a Dhall expression that evaluates to a Text literal++Available options:+  --file FILE              Read expression from a file instead of standard input+  --output FILE            Write result to a file instead of standard output+  -h,--help                Show this help text+```
+ TESTING.md view
@@ -0,0 +1,185 @@+# Testing Updo++Assuming this layout of make files in updo and at the root of your project.++```+├── updo+│   ├── Makefile+│   ├── project-dhall+│   │   └── Makefile+│   ├── project-dhall2config+│   │   ├── Makefile+│   └── project-nix+│       └── Makefile+├── project-files.mk+└── project-versions.mk+```++We'll need some versions, something like:++```+$ cat project-versions.mk +# Versions of GHC and stackage resolver, the ones we're on and the next ones+# we're upgrading to.+GHC_VERSION := 8.10.7+GHC_UPGRADE := 9.2.5+STACKAGE_VERSION := lts-18.27+STACKAGE_UPGRADE := lts-20.5+```++We'll need to set up our `project-files.mk`:++```make+# How to generate project-nix/ghc-$(GHC_VERSION)/sha256map.nix?+# This is copied from ghc-$(GHC_VERSION).sha256map.nix.+#  - false to generate from *.dhall inputs via sha256map.hs.+#  - true to generate from stack.yaml via sha256map.py.+SHA256MAP_VIA_PYTHON := false++include project-versions.mk+include updo/Makefile++project-nix/ghc-%/sha256map.nix: ghc-%.sha256map.nix+	mkdir -p $(@D) && cp $^ $@++.PHONY: all+all: \+  projects \+  project-nix/ghc-$(GHC_VERSION)/sha256map.nix++# To make stack.yaml or cabal.project and no other, mark the file we copy from+# as intermediate. This is all we want when not doing a GHC upgrade.+#+# Comment out these .INTERMEDIATE targets to allow these files to be kept.+.INTERMEDIATE: ghc-$(GHC_VERSION).$(CABAL_VIA).project+.INTERMEDIATE: ghc-$(GHC_UPGRADE).$(CABAL_VIA).project+.INTERMEDIATE: ghc-$(GHC_VERSION).$(STACK_VIA).yaml+.INTERMEDIATE: ghc-$(GHC_UPGRADE).$(STACK_VIA).yaml+.INTERMEDIATE: ghc-$(GHC_VERSION).sha256map.nix+.INTERMEDIATE: ghc-$(GHC_UPGRADE).sha256map.nix++# Alternative targets for generating project files (not recommended):+#  - ghc-x.y.z.stack2cabal.project+#  - ghc-x.y.z.cabal2stack.yaml+#  - ghc-x.y.z.dhall2yaml2stack.yaml+include updo/alternatives/cabal2stack/Makefile+include updo/alternatives/stack2cabal/Makefile+include updo/alternatives/yaml2stack/Makefile++# If true, generate the sha256map from the stack.yaml with python,+# overriding the recipe for this target.+ifeq ($(SHA256MAP_VIA_PYTHON), true)+ghc-$(GHC_VERSION).sha256map.nix: stack.yaml+	updo/project-nix/sha256map.py <$^ >$@+ghc-$(GHC_UPGRADE).sha256map.nix: stack.yaml+	updo/project-nix/sha256map.py <$^ >$@+endif++.DEFAULT_GOAL := all+```++## Default Targets++The default targets are `stack.yaml` and `cabal.project`.++* [ ] Can we build just those without leaving behind[^check-temp-files] any+      temporary files?++[^check-temp-files]: You could check for unwanted files with `git status`.++```+$ make -f project-files.mk all --always-make+$ make -f project-files.mk all+```++## All Possible Targets++Comment out the `.INTERMEDIATE` targets in `project-files.mk` and then:++```+$ make -f project-files.mk all-possible-projects --always-make --jobs+```++* [ ] Does this make two sets of each of the following?++- `ghc-x.y.z.dhall2cabal.project`+- `ghc-x.y.z.dhall2config.project`+- `ghc-x.y.z.dhall2stack.yaml`+- `ghc-x.y.z.dhall2stack.yaml.lock`+- `ghc-x.y.z.sha256map.nix`++## Clean++* [ ] After making `all-possible-projects`, can we clean all of these?++```+$ make -f project-files.mk clean+```++## SHA Map Targets++* [ ] Can we build `ghc-x.y.z.sha256map.nix`?++```+$ make -f project-files.mk project-sha256maps+```++Try again flipping the sense of the variable SHA256MAP_VIA_PYTHON or by setting+it on the command line:++```+$ make -f project-files.mk ghc-x.y.z.sha256map.nix --always-make SHA256MAP_VIA_PYTHON=true+```++* [ ] Is it generating maps using another script, the Python one instead of the Haskell one?+* [ ] Are the entries in the generated map sorted in both cases?++This should make two of `ghc-x.y.z.sha256map.nix`[^version_only]. You might like to use those+file names as targets explicitly.++[^version_only]: For now we only provide the option to pick which script to use+  to generate `ghc-$(GHC_VERSION).sha256map.nix`. The+  `ghc-$(GHC_UPGRADE).sha256map.nix` is only ever generated using the Haskell+  script.++## Sorting Packages Works++We need to run a Haskell script to generate `.updo/pkgs-sorted.dhall`.  After a+cabal clean, this script will need to download dependencies.++* [ ] Can it do that without polluting the generated file with status updates from cabal saying+      that it has downloaded this or that dependency[^silent-script]?+      +[^silent-script]: The Haskell script must run silently for this to work.++```+$ cabal clean+$ make -f project-files.mk pkgs-sorted --always-make+updo/project-dhall/pkgs-sorted.hs > .updo/pkgs-sorted.dhall+```++## Upgrade Packages Done Works++Generated files, not for source control are written to  `.updo`.++* [ ] Can we generate `.updo/pkgs-upgrade-done.dhall`?+* [ ] Does adding or removing packages from+      `project-dhall/pkgs-upgrade-todo.dhall` change the list of packages in+      `.updo/pkgs-upgrade-done.dhall`?++```+$ make -f project-files.mk pkgs-upgrade-done --always-make+updo/project-dhall/pkgs-sorted.hs > .updo/pkgs-sorted.dhall+./updo/project-dhall/pkgs-upgrade-done.hs \+  ./.updo/pkgs-sorted.dhall \+  ./project-dhall/pkgs-upgrade-todo.dhall \+  > .updo/pkgs-upgrade-done.dhall+```++## Alternative Targets++* [ ] Can we build these targets?++- `ghc-x.y.z.dhall2yaml2stack.yaml`+- `ghc-x.y.z.stack2cabal.project`+- `ghc-x.y.z.cabal2stack.yaml`
+ alternatives/cabal2stack/Makefile view
@@ -0,0 +1,19 @@+.PHONY: cabal2stack-projects+cabal2stack-projects: \+  ghc-$(GHC_VERSION).cabal2stack.yaml \+  ghc-$(GHC_VERSION).cabal2stack.yaml.lock \+  ghc-$(GHC_UPGRADE).cabal2stack.yaml \+  ghc-$(GHC_UPGRADE).cabal2stack.yaml.lock++ghc-$(GHC_VERSION).cabal2stack.yaml: ghc-$(GHC_VERSION).$(CABAL_VIA).project+	rm -rf dist-newstyle/cache+	cabal build all --enable-tests --project-file=$< --dry-run+	cabal2stack --resolver=$(STACKAGE_VERSION) --allow-newer --output $@ --plan-json dist-newstyle/cache/plan.json++ghc-$(GHC_UPGRADE).cabal2stack.yaml: ghc-$(GHC_UPGRADE).$(CABAL_VIA).project+	rm -rf dist-newstyle/cache+	cabal build all --enable-tests --project-file=$< --dry-run+	cabal2stack --resolver=$(STACKAGE_UPGRADE) --allow-newer --output $@ --plan-json dist-newstyle/cache/plan.json++ghc-%.cabal2stack.yaml.lock: ghc-%.cabal2stack.yaml+	stack build --test --no-run-tests --bench --no-run-benchmarks --dry-run --stack-yaml $<
+ alternatives/cabal2stack/README.md view
@@ -0,0 +1,4 @@+# Updo Cabal2Stack++Using [cabal2stack](https://github.com/iconnect/cabal2stack), the `Makefile` has+targets for `ghc-x.y.z.cabal2stack.yaml` and its `.lock` file.
+ alternatives/stack2cabal/Makefile view
@@ -0,0 +1,12 @@+.PHONY: stack2cabal-projects+stack2cabal-projects: \+  ghc-$(GHC_VERSION).stack2cabal.project \+  ghc-$(GHC_UPGRADE).stack2cabal.project++ghc-$(GHC_VERSION).stack2cabal.project: ghc-$(GHC_VERSION).$(STACK_VIA).yaml+	stack2cabal --file $< --output-file $@++ghc-$(GHC_UPGRADE).stack2cabal.project: ghc-$(GHC_UPGRADE).$(STACK_VIA).yaml+	stack2cabal --file $< --output-file $@++ghc-%.stack2cabal.project.freeze: ghc-%.stack2cabal.project
+ alternatives/stack2cabal/README.md view
@@ -0,0 +1,4 @@+# Updo Stack2Cabal++Using [stack2cabal](https://github.com/hasufell/stack2cabal), the `Makefile` has+targets for `ghc-x.y.z.stack2cabal.project`.
+ alternatives/yaml2stack/Makefile view
@@ -0,0 +1,74 @@+# When the prerequisites are a function and a list, we echo a dhall expression+# that calls this function with the list. Both function and list are in dhall+# files that must be ./ prefixed to be imported into the expression.+#+# $(^:%=./%) does the ./ prefixing that dhall needs to recognize the imports.+#+# "The import resolution phase replaces all imports with the expression located+# at that import, transitively resolving imports within the imported expression+# if necessary."+# SOURCE:https://github.com/dhall-lang/dhall-lang/blob/master/standard/imports.md#import-resolution-judgment++UPDO_TMP := .updo+Y2S_TMP := $(UPDO_TMP)/dhall2yaml2stack++.PHONY: dhall2yaml2stack-projects+dhall2yaml2stack-projects: \+  ghc-$(GHC_VERSION).dhall2yaml2stack.yaml \+  ghc-$(GHC_VERSION).dhall2yaml2stack.yaml.lock \+  ghc-$(GHC_UPGRADE).dhall2yaml2stack.yaml \+  ghc-$(GHC_UPGRADE).dhall2yaml2stack.yaml.lock++.INTERMEDIATE: $(Y2S_TMP)/ghc-$(GHC_VERSION)/base.yaml+$(Y2S_TMP)/ghc-$(GHC_VERSION)/base.yaml: \+  project-dhall/ghc-$(GHC_VERSION)/text-templates/stacksnippet.dhall+	mkdir -p $(@D) && echo '$(<:%=./%) (Some "$(STACKAGE_VERSION)" : Optional Text)' | dhall text --output $@++.INTERMEDIATE: $(Y2S_TMP)/ghc-$(GHC_UPGRADE)/base.yaml+$(Y2S_TMP)/ghc-$(GHC_UPGRADE)/base.yaml: \+  project-dhall/ghc-$(GHC_UPGRADE)/text-templates/stacksnippet.dhall+	mkdir -p $(@D) && echo '$(<:%=./%) (Some "$(STACKAGE_UPGRADE)" : Optional Text)' | dhall text --output $@++# Mirror pkgs from dhall to yaml.+.INTERMEDIATE: $(Y2S_TMP)/ghc-$(GHC_VERSION)/pkgs.yaml+$(Y2S_TMP)/ghc-$(GHC_VERSION)/pkgs.yaml: \+  updo/text-templates/yaml2stack/pkgs.dhall \+  $(UPDO_TMP)/pkgs-sorted.dhall+	echo "$(^:%=./%)" | dhall text --output $@++.INTERMEDIATE: $(Y2S_TMP)/ghc-$(GHC_UPGRADE)/pkgs.yaml+$(Y2S_TMP)/ghc-$(GHC_UPGRADE)/pkgs.yaml: \+  updo/text-templates/yaml2stack/pkgs.dhall \+  $(UPDO_TMP)/pkgs-upgrade-done.dhall+	echo "$(^:%=./%)" | dhall text --output $@++# Mirror constraints from dhall to yaml.+$(Y2S_TMP)/ghc-%/constraints.yaml: \+  updo/text-templates/yaml2stack/constraints.dhall \+  project-dhall/ghc-%/constraints.dhall+	echo "$(^:%=./%)" | dhall text --output $@++# Mirror source repositories from dhall to yaml.+$(Y2S_TMP)/ghc-%.yaml: \+  updo/text-templates/yaml2stack/repo-items.dhall \+  project-dhall/ghc-%.dhall+	echo "$(^:%=./%)" | dhall text --output $@++# Join the YAML fragments into a single file, the stack project.+ghc-%.dhall2yaml2stack.yaml: \+  $(Y2S_TMP)/ghc-%/base.yaml \+  $(Y2S_TMP)/ghc-%/pkgs.yaml \+  $(Y2S_TMP)/ghc-%/constraints.yaml \+  $(Y2S_TMP)/ghc-%/deps-external.yaml \+  $(Y2S_TMP)/ghc-%/deps-internal.yaml \+  $(Y2S_TMP)/ghc-%/forks-external.yaml \+  $(Y2S_TMP)/ghc-%/forks-internal.yaml \+  updo/alternatives/yaml2stack/join-sort-fragments.sh+	./updo/alternatives/yaml2stack/join-sort-fragments.sh $(<D) >$@++ghc-%.dhall2yaml2stack.yaml.lock: ghc-%.dhall2yaml2stack.yaml+	stack build --test --no-run-tests --bench --no-run-benchmarks --dry-run --stack-yaml $<++# To keep dhall to yaml intermediate files, uncomment the following (no need to+# comment out the .INTERMEDIATE lines above):+# .SECONDARY: $(wildcard $(Y2S_TMP)/*.yaml)
+ alternatives/yaml2stack/README.md view
@@ -0,0 +1,13 @@+# Updo Dhall2Yaml2Stack++The `Makefile` has targets for `ghc-x.y.z.dhall2yaml2stack.yaml` and its `.lock`+file.++The `.dhall` files are mirrored to `.yaml` files[^working-dir] followed by a very lightweight+stitching together of these fragments with+[yq](https://mikefarah.gitbook.io/yq/), removing comments and sorting+dependencies.++[^working-dir]: The `.yaml` fragments are written to `.updo/dhall2yaml2stack`,+copied to a bash `$(mktemp -d)` temporary directory and then stitched together+there. There's a note in the make file explaining how to keep these fragments.
+ alternatives/yaml2stack/join-sort-fragments.sh view
@@ -0,0 +1,26 @@+#!/bin/sh+set -eu++WORKDIR=$(mktemp -d)+trap 'rm -rf "$WORKDIR"' EXIT++cd "$1"++for f in *.yaml+do+    cp "$PWD/${f}" "$WORKDIR/${f}"+done++cd "$WORKDIR"+for f in *.yaml+do+    yq '... comments=""' --inplace --prettyPrint "${f}"+done++yq '.extra-deps |= sort' --inplace constraints.yaml+yq '.packages |= sort_by(sub("\/", ""))' --inplace pkgs.yaml+yq '.extra-deps |= sort_by(.git | sub("(.*\.com:|.*\.com\/)(.+$)", "$2"))' --inplace deps-external.yaml+yq '.extra-deps |= sort_by(.git | sub("(.*\.com:|.*\.com\/)(.+$)", "$2"))' --inplace deps-internal.yaml+yq '.extra-deps |= sort_by(.git | sub("(.*\.com:|.*\.com\/)(.+$)", "$2"))' --inplace forks-external.yaml+yq '.extra-deps |= sort_by(.git | sub("(.*\.com:|.*\.com\/)(.+$)", "$2"))' --inplace forks-internal.yaml+yq eval-all '. as $item ireduce ({}; . *+ $item )' base.yaml pkgs.yaml deps-external.yaml deps-internal.yaml forks-external.yaml forks-internal.yaml constraints.yaml
+ package.dhall view
@@ -0,0 +1,34 @@+{ name = "updo"+, version = "1.0.0"+, synopsis = "A style of maintaining and upgrading Haskell projects"+, description =+    ''+    From configuration in .dhall, generate projects (stack.yaml and+    cabal.project) and progressively upgrade projects.+    ''+, category = "Development"+, github = "cabalism/updo"+, author = "Phil de Joux"+, maintainer = "phil.dejoux@blockscope.com"+, copyright = "© 2023 Phil de Joux, © 2023 Block Scope Limited"+, tested-with =+  [ "GHC == 8.6.5", "GHC == 8.10.7", "GHC == 9.2.8", "GHC == 9.4.5" ]+, extra-source-files =+  [ "**/*.md", "**/*.dhall", "**/Makefile", "**/*hs", "**/*.py", "**/*.sh" ]+, dependencies =+  [ "aeson >= 2.1.2 && <2.2"+  , "base >=4.12 && <5"+  , "dhall >= 1.41.1 && <1.42"+  , "filepath >= 1.4.2 && <1.5"+  , "text >= 1.2.3 && <2.1"+  , "turtle >= 1.6.1 && <1.7"+  , "utf8-string >= 1.0.2 && <1.1"+  ]+, executables =+  { updo-pkgs-sorted.main = "project-dhall/pkgs-sorted.hs"+  , updo-pkgs-upgrade-done.main = "project-dhall/pkgs-upgrade-done.hs"+  , updo-pkgs-upgrade-partition.main = "project-dhall/pkgs-upgrade-partition.hs"+  , updo-pkg-groups.main = "project-dhall2config/pkg-groups.hs"+  , updo-sha256map.main = "project-nix/sha256map.hs"+  }+}
+ project-dhall/Makefile view
@@ -0,0 +1,76 @@+UPDO_TMP := ./.updo++ifeq ($(PKGS_SORTED_HS_EXE), true)+PKGS_SORTED_HS := updo-pkgs-sorted+else+PKGS_SORTED_HS := ./updo/project-dhall/pkgs-sorted.hs+endif++ifeq ($(PKGS_UPGRADE_DONE_HS_EXE), true)+PKGS_UPGRADE_DONE_HS := updo-pkgs-upgrade-done+else+PKGS_UPGRADE_DONE_HS := ./updo/project-dhall/pkgs-upgrade-done.hs+endif++.PHONY: dhall2stack-projects+dhall2stack-projects: \+  ghc-$(GHC_VERSION).dhall2stack.yaml \+  ghc-$(GHC_VERSION).dhall2stack.yaml.lock \+  ghc-$(GHC_UPGRADE).dhall2stack.yaml \+  ghc-$(GHC_UPGRADE).dhall2stack.yaml.lock \++.PHONY: dhall2cabal-projects+dhall2cabal-projects: \+  ghc-$(GHC_VERSION).dhall2cabal.project \+  ghc-$(GHC_UPGRADE).dhall2cabal.project++.PHONY: pkgs-sorted+pkgs-sorted: $(UPDO_TMP)/pkgs-sorted.dhall++.PHONY: pkgs-upgrade-done+pkgs-upgrade-done: $(UPDO_TMP)/pkgs-upgrade-done.dhall++$(UPDO_TMP)/pkgs-sorted.dhall: \+  project-dhall/pkg-groups.dhall \+  project-dhall/pkgs/*.dhall+	mkdir -p $(@D) && $(PKGS_SORTED_HS) > $@++$(UPDO_TMP)/pkgs-upgrade-done.dhall: \+  project-dhall/pkgs/*.dhall \+  $(UPDO_TMP)/pkgs-sorted.dhall \+  project-dhall/pkgs-upgrade-todo.dhall+	./updo/project-dhall/pkgs-upgrade-done.hs \+	  ./$(UPDO_TMP)/pkgs-sorted.dhall \+	  ./project-dhall/pkgs-upgrade-todo.dhall \+	  > $@++ghc-$(GHC_VERSION).dhall2stack.yaml: \+  project-dhall/ghc-$(GHC_VERSION)/text-templates/dhall2stack.dhall \+  $(UPDO_TMP)/pkgs-sorted.dhall \+  project-dhall/ghc-$(GHC_VERSION)/*.dhall \+  updo/text-templates/stack/*.dhall+	echo './$< ./$(UPDO_TMP)/pkgs-sorted.dhall "$(STACKAGE_VERSION)"' | dhall text --output $@++ghc-$(GHC_UPGRADE).dhall2stack.yaml: \+  project-dhall/ghc-$(GHC_UPGRADE)/text-templates/dhall2stack.dhall \+  $(UPDO_TMP)/pkgs-upgrade-done.dhall \+  project-dhall/ghc-$(GHC_UPGRADE)/*.dhall \+  updo/text-templates/stack/*.dhall+	echo './$< ./$(UPDO_TMP)/pkgs-upgrade-done.dhall "$(STACKAGE_UPGRADE)"' | dhall text --output $@++ghc-$(GHC_VERSION).dhall2cabal.project: \+  project-dhall/ghc-$(GHC_VERSION)/text-templates/dhall2cabal.dhall \+  $(UPDO_TMP)/pkgs-sorted.dhall \+  project-dhall/ghc-$(GHC_VERSION)/*.dhall \+  updo/text-templates/cabal/*.dhall+	echo './$< ./$(UPDO_TMP)/pkgs-sorted.dhall "$(STACKAGE_VERSION)"' | dhall text --output $@++ghc-$(GHC_UPGRADE).dhall2cabal.project: \+  project-dhall/ghc-$(GHC_UPGRADE)/text-templates/dhall2cabal.dhall \+  $(UPDO_TMP)/pkgs-upgrade-done.dhall \+  project-dhall/ghc-$(GHC_UPGRADE)/*.dhall \+  updo/text-templates/cabal/*.dhall+	echo './$< ./$(UPDO_TMP)/pkgs-upgrade-done.dhall "$(STACKAGE_UPGRADE)"' | dhall text --output $@++ghc-%.dhall2stack.yaml.lock: ghc-%.dhall2stack.yaml+	stack build  --test --no-run-tests --bench --no-run-benchmarks --dry-run --stack-yaml $<
+ project-dhall/README.md view
@@ -0,0 +1,7 @@+# Updo Dhall++The `Makefile` has targets for stack and cabal projects generated using Dhall+text templating:++* `ghc-x.y.z.dhall2stack.yaml`+* `ghc-x.y.z.dhall2cabal.project`
+ project-dhall/pkgs-sorted.hs view
@@ -0,0 +1,27 @@+#!/usr/bin/env stack+-- stack script --resolver=lts-18.27 --package=base --package=dhall --package=filepath --package=text+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | Description: Sorts packages as a flat list.+module Main where++import Data.List (concat, sortOn)+import qualified Data.Text as T (filter, pack)+import qualified Data.Text.IO as T (putStrLn)+import Dhall (Text, auto, embed, inject, input)+import Dhall.Core (pretty)+import System.FilePath ((<.>), (</>))++grab :: String -> IO [Text]+grab name = input auto (T.pack $ "./project-dhall/pkgs" </> name <.> "dhall")++main :: IO ()+main = do+    pkgGroups :: [String] <- input auto "./project-dhall/pkg-groups.dhall"+    xs <- traverse grab pkgGroups+    -- Package items are (usually in this project) paths to folders containing a+    -- .cabal file.  These aren't canonical so may or may not have a trailing+    -- slash.  To sort them consistently, we filter out the slashes.+    let sortedPkgPaths = sortOn (T.filter (/= '/')) $ concat xs+    T.putStrLn . pretty $ embed inject sortedPkgPaths
+ project-dhall/pkgs-upgrade-done.hs view
@@ -0,0 +1,20 @@+#!/usr/bin/env stack+-- stack script --resolver=lts-18.27 --package=base --package=dhall --package=text+{-# LANGUAGE ScopedTypeVariables #-}++-- | Description: The difference between all packages and those in the todo+-- list, the packgaes that have been upgraded.+module Main where++import Data.List ((\\))+import qualified Data.Text as T (pack)+import qualified Data.Text.IO as T (putStrLn)+import Dhall (Text, auto, embed, inject, input)+import Dhall.Core (pretty)+import System.Environment (getArgs)++main = do+    sortedFile : todoFile : _ <- getArgs+    sorted :: [Text] <- input auto (T.pack sortedFile)+    todo :: [Text] <- input auto (T.pack todoFile)+    T.putStrLn . pretty $ embed inject (sorted \\ todo)
+ project-dhall/pkgs-upgrade-partition.hs view
@@ -0,0 +1,34 @@+#!/usr/bin/env stack+-- stack script --resolver=lts-18.27 --package=base --package=dhall --package=text+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | Description: Partition the packages into those that have been upgraded and+-- those that have not.+module Main where++import Data.List (intersect, (\\))+import qualified Data.Text as T (pack)+import qualified Data.Text.IO as T (putStrLn)+import Dhall (FromDhall, Generic, Text, ToDhall, auto, embed, inject, input)+import Dhall.Core (pretty)+import System.Environment (getArgs)++data PkgUpgrade = PkgUpgrade+    { pkgs :: [Text]+    , done :: [Text]+    , todo :: [Text]+    }+    deriving (Show, Generic, ToDhall, FromDhall)++main :: IO ()+main = do+    file : _ <- getArgs+    pkgs :: [Text] <- input auto (T.pack file)+    remaining :: [Text] <- input auto "./project-dhall/pkgs-upgrade-todo.dhall"+    let todo = pkgs `intersect` remaining+    let done = pkgs \\ todo+    T.putStrLn . pretty $ embed inject PkgUpgrade{..}
+ project-dhall/pkgs-upgrade.dhall view
@@ -0,0 +1,65 @@+-- NOTE: With cabal-3.10, imports are relative to each other but imported+-- packages are not, they are still relative to the project root. With+-- cabal-3.8, the first version with imports, imports are relative to the+-- project root too.+let TYPES = ../types.dhall++in  \(pkg-import : TYPES.CabalRelativity) ->+    \(ghc-version : Text) ->+    \(ghc-upgrade : Text) ->+    \(x : { pkgs : List Text, done : List Text, todo : List Text }) ->+      let N = https://prelude.dhall-lang.org/Natural/package.dhall++      let L = https://prelude.dhall-lang.org/List/package.dhall++      let concatMapSep = https://prelude.dhall-lang.org/Text/concatMapSep++      let relativePkg =+            merge+              { CabalProjectRelative = \(s : Text) -> "./${s}"+              , CabalImportRelative = \(s : Text) -> "./${s}"+              }+              pkg-import++      let pkgList =+            \(indent : Text) ->+            \(xs : List Text) ->+              concatMapSep+                ''++                ${indent}, ''+                Text+                relativePkg+                xs++      let pkgListComment =+            \(xs : List Text) ->+              concatMapSep "\n" Text (\(s : Text) -> "-- ${relativePkg s}") xs++      in  if        L.null Text x.todo+                ||  N.equal (L.length Text x.pkgs) (L.length Text x.done)+          then  ''+                packages:+                    ${pkgList "  " x.pkgs}+                ''+          else      ''+                    if impl(ghc <= ${ghc-version})+                      packages:+                          ${pkgList "    " x.pkgs}+                    ''+                ++  ( if    L.null Text x.done+                      then  ""+                      else  ''+                            else+                              packages:+                                  ${pkgList "    " x.done}+                            ''+                    )+                ++  ( if    L.null Text x.done+                      then  ""+                      else  ''++                            -- TODO for impl(ghc >= ${ghc-upgrade})+                            ${pkgListComment x.todo}+                            ''+                    )
+ project-dhall2config/Makefile view
@@ -0,0 +1,75 @@+ifeq ($(PKG_GROUPS_HS_EXE), true)+PKG_GROUPS_HS := updo-pkg-groups+else+PKG_GROUPS_HS := ./updo/project-dhall2config/pkg-groups.hs+endif++.PHONY: dhall2config-projects+dhall2config-projects: \+  ghc-$(GHC_VERSION).dhall2config.project \+  ghc-$(GHC_UPGRADE).dhall2config.project++project-cabal/pkgs.config: \+  updo/text-templates/cabal/pkg-groups.dhall \+  project-dhall/pkg-groups.dhall \+  $(patsubst project-dhall/pkgs/%.dhall, project-cabal/pkgs/%.config, $(wildcard project-dhall/pkgs/*.dhall))+  ifeq ($(CABAL_RELATIVITY), ImportRelative)+	echo 'let f = ./updo/types/CabalRelativity.dhall in ./$< f.CabalImportRelative ./project-dhall/pkg-groups.dhall' | dhall text --output $@+  else+	echo 'let f = ./updo/types/CabalRelativity.dhall in ./$< f.CabalProjectRelative ./project-dhall/pkg-groups.dhall' | dhall text --output $@+  endif++generate-pkg-configs: \+  project-dhall/pkg-groups.dhall \+  project-dhall/pkgs-upgrade-todo.dhall+  ifeq ($(CABAL_RELATIVITY), ImportRelative)+	mkdir -p project-cabal/pkgs && $(PKG_GROUPS_HS) "let f = ./updo/types/CabalRelativity.dhall in f.CabalImportRelative" "$(GHC_VERSION)" "$(GHC_UPGRADE)"+  else+	mkdir -p project-cabal/pkgs && $(PKG_GROUPS_HS) "let f = ./updo/types/CabalRelativity.dhall in f.CabalProjectRelative" "$(GHC_VERSION)" "$(GHC_UPGRADE)"+  endif++# Mirror packages (package groups) from dhall to config with+# generate-pkg-configs doing the work once for all package groups.+#+# The "target: prerequisites ;" explicitly defines an empty recipe.+# SEE: https://www.gnu.org/software/make/manual/html_node/Empty-Recipes.html+project-cabal/pkgs/%.config: project-dhall/pkgs/%.dhall generate-pkg-configs ;++# Mirror constraints from dhall to config. Any revisions in the constraints are+# stripped, like this for example:+# - hedgehog-quickcheck ==0.1.1@rev:4+# + hedgehog-quickcheck ==0.1.1+project-cabal/ghc-%/constraints.config : \+  updo/text-templates/cabal/package.dhall \+  project-dhall/ghc-%/constraints.dhall+	mkdir -p $(@D)+	echo "let f = ./$< in f.constraints ./project-dhall/ghc-$*/constraints.dhall" \+	| dhall text | sed "s/@rev:.*//" >$@++# Mirror source repositories from dhall to config.+project-cabal/ghc-%.config: \+  updo/text-templates/cabal/package.dhall \+  project-dhall/ghc-%.dhall+	mkdir -p $(@D) && echo "let f = ./$< in f.repo-items ./project-dhall/ghc-$*.dhall" | dhall text --output $@++ghc-$(GHC_VERSION).dhall2config.project: \+  project-dhall/ghc-$(GHC_VERSION)/text-templates/dhall2config.dhall \+  updo/text-templates/dhall2config.dhall \+  project-cabal/ghc-$(GHC_VERSION)/constraints.config \+  project-cabal/ghc-$(GHC_VERSION)/deps-external.config \+  project-cabal/ghc-$(GHC_VERSION)/deps-internal.config \+  project-cabal/ghc-$(GHC_VERSION)/forks-external.config \+  project-cabal/ghc-$(GHC_VERSION)/forks-internal.config \+  project-cabal/pkgs.config+	echo './$< "$(STACKAGE_VERSION)" "$(GHC_VERSION)"' | dhall text --output $@++ghc-$(GHC_UPGRADE).dhall2config.project: \+  project-dhall/ghc-$(GHC_UPGRADE)/text-templates/dhall2config.dhall \+  updo/text-templates/dhall2config.dhall \+  project-cabal/ghc-$(GHC_UPGRADE)/constraints.config \+  project-cabal/ghc-$(GHC_UPGRADE)/deps-external.config \+  project-cabal/ghc-$(GHC_UPGRADE)/deps-internal.config \+  project-cabal/ghc-$(GHC_UPGRADE)/forks-external.config \+  project-cabal/ghc-$(GHC_UPGRADE)/forks-internal.config \+  project-cabal/pkgs.config+	echo './$< "$(STACKAGE_UPGRADE)" "$(GHC_UPGRADE)"' | dhall text --output $@
+ project-dhall2config/README.md view
@@ -0,0 +1,27 @@+# Updo Cabal++We offer two ways of generating cabal projects, `dhall2cabal` and+`dhall2config`. This folder deals with the later.++The `Makefile` has targets for `ghc-x.y.z.dhall2config.project` that is mostly+imports of `.config` files generated from the `.dhall` configuration, something+like:++```+import: project-stackage/stackage-resolver.config++import: project-cabal/pkgs.config++import: project-cabal/ghc-x.y.z/constraints.config+import: project-cabal/ghc-x.y.z/deps-external.config+import: project-cabal/ghc-x.y.z/deps-internal.config+import: project-cabal/ghc-x.y.z/forks-external.config+import: project-cabal/ghc-x.y.z/forks-internal.config++build-info: True+```++For this rule using `make --jobs`[^parallel-make] will speed up the recipe a+lot.++[^parallel-make]: https://www.gnu.org/software/make/manual/html_node/Parallel.html
+ project-dhall2config/pkg-groups.hs view
@@ -0,0 +1,52 @@+#!/usr/bin/env stack+-- stack script --resolver=lts-18.27 --package=base --package=dhall --package=filepath --package=text++{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | Description: Convert each package group from .dhall to .config with upgrade+-- conditionals if needed.+module Main where++import Control.Monad (forM_)+import Data.List (concat, filter, intersect, sortOn, (\\))+import qualified Data.Text as T (pack, unpack)+import qualified Data.Text.IO as T (putStrLn)+import Dhall (FromDhall, Generic, Text, ToDhall, auto, embed, inject, input)+import Dhall.Core (pretty)+import System.Environment (getArgs)+import System.FilePath ((<.>), (</>))++type UpgradeConfig = CabalRelativity -> Text -> Text -> PkgUpgrade -> Text++data PkgUpgrade = PkgUpgrade+    { pkgs :: [Text]+    , done :: [Text]+    , todo :: [Text]+    }+    deriving (Show, Generic, ToDhall, FromDhall)++data CabalRelativity = CabalProjectRelative | CabalImportRelative+    deriving (Show, Generic, ToDhall, FromDhall)++groupUpgrade :: [Text] -> String -> IO PkgUpgrade+groupUpgrade remaining name = do+    pkgs :: [Text] <- input auto (T.pack $ "./project-dhall/pkgs" </> name <.> "dhall")+    let todo = pkgs `intersect` remaining+    let done = pkgs \\ todo+    return PkgUpgrade{..}++main :: IO ()+main = do+    cabalRelativity : ghcVersion : ghcUpgrade : _ <- getArgs+    remaining :: [Text] <- input auto "./project-dhall/pkgs-upgrade-todo.dhall"+    relativity :: CabalRelativity <- input auto $ T.pack cabalRelativity+    pkgUpgrade :: UpgradeConfig <- input auto "./updo/project-dhall/pkgs-upgrade.dhall"+    pkgGroups :: [String] <- input auto "./project-dhall/pkg-groups.dhall"+    forM_ pkgGroups $ \p -> do+        up <- groupUpgrade remaining p+        let config = pkgUpgrade relativity (T.pack ghcVersion) (T.pack ghcUpgrade) up+        writeFile ("./project-cabal/pkgs" </> p <.> "config") (T.unpack config)
+ project-nix/Makefile view
@@ -0,0 +1,19 @@+ifeq ($(SHA256MAP_HS_EXE), true)+SHA256MAP_HS := updo-sha256map+else+SHA256MAP_HS := ./updo/project-nix/sha256map.hs+endif++project-sha256maps: \+  ghc-$(GHC_VERSION).sha256map.nix \+  ghc-$(GHC_UPGRADE).sha256map.nix++ghc-%.sha256map.nix: \+  project-dhall/ghc-%/deps-external.dhall \+  project-dhall/ghc-%/deps-internal.dhall \+  project-dhall/ghc-%/forks-external.dhall \+  project-dhall/ghc-%/forks-internal.dhall+	echo '[$(^:%=./%,) ([] : List {loc : Text, tag : Text, sub : List Text})]' | $(SHA256MAP_HS) > $@++project-versions.nix:+	echo './updo/project-nix/project-versions.dhall "$(GHC_VERSION)" "$(GHC_UPGRADE)"' | dhall text > $@
+ project-nix/README.md view
@@ -0,0 +1,67 @@+# Updo Nix++The `Makefile` has targets for `ghc-x.y.z.sha256map.nix` which has the form:++```nix+{+  "repo-url"."commit-rev" = "sha256";+}+```++This can be used with [haskell.nix][haskell-nix].  We grab the sha256 hash with+`nix-prefetch-git` from the shell so you'll need that installed.++[haskell-nix]: https://input-output-hk.github.io/haskell.nix/tutorials/source-repository-hashes.html#avoiding-modifying-cabalproject-and-stackyaml++```+$ nix-prefetch-git --help+syntax: nix-prefetch-git [options] [URL [REVISION [EXPECTED-HASH]]]++Options:+      --out path      Path where the output would be stored.+      --url url       Any url understood by 'git clone'.+      --rev ref       Any sha1 or references (such as refs/heads/master)+      --hash h        Expected hash.+      --branch-name   Branch name to check out into+      --sparse-checkout Only fetch and checkout part of the repository.+      --deepClone     Clone the entire repository.+      --no-deepClone  Make a shallow clone of just the required ref.+      --leave-dotGit  Keep the .git directories.+      --fetch-lfs     Fetch git Large File Storage (LFS) files.+      --fetch-submodules Fetch submodules.+      --builder       Clone as fetchgit does, but url, rev, and out option are mandatory.+      --quiet         Only print the final json summary.+```++```+$ nix-prefetch-git https://github.com/input-output-hk/haskell.nix.git bc01ebc05a8105035c9449943046b46c8364b932+...+{+  "url": "https://github.com/input-output-hk/haskell.nix.git",+  "rev": "bc01ebc05a8105035c9449943046b46c8364b932",+  "date": "2019-05-30T13:13:18+08:00",+  "sha256": "003lm3pm024vhbfmii7xcdd9v2rczpflxf7gdl2pyxia7p014i8z",+  "fetchSubmodules": false+}+```++We also include a `sha256map.py` script from haskell.nix[^sorted] but this is 4+times slower.++The `Makefile` also has `project-versions.nix` as a target.++```nix+{ current-version =+    { ghc-xyz-project-stack = "stack.yaml";+      ghc-xyz-project-cabal = "cabal.project";+      ghc-xyz-sha256map = import ./project-nix/ghc-u.v.w/sha256map.nix;+    };+  upgrade-version =+    { ghc-xyz-project-stack = "stack.upgrade.yaml";+      ghc-xyz-project-cabal = "cabal.upgrade.project";+      ghc-xyz-sha256map = import ./project-nix/ghc-x.y.z/sha256map.nix;+    };+}+```++[^sorted]: Renamed from `sha256map-regenerate.py` and modified to sort the map by keys.
+ project-nix/project-versions.dhall view
@@ -0,0 +1,15 @@+\(ghc-version : Text) ->+\(ghc-upgrade : Text) ->+  ''+  { current-version =+      { ghc-xyz-project-stack = "stack.yaml";+        ghc-xyz-project-cabal = "cabal.project";+        ghc-xyz-sha256map = import ./project-nix/ghc-${ghc-version}/sha256map.nix;+      };+    upgrade-version =+      { ghc-xyz-project-stack = "stack.upgrade.yaml";+        ghc-xyz-project-cabal = "cabal.upgrade.project";+        ghc-xyz-sha256map = import ./project-nix/ghc-${ghc-upgrade}/sha256map.nix;+      };+  }+  ''
+ project-nix/sha256map.hs view
@@ -0,0 +1,76 @@+#!/usr/bin/env stack+-- stack script --resolver=lts-18.27 --package=aeson --package=base --package=dhall --package=text --package=turtle --package=utf8-string+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | Description: Generates a sha256map.+module Main where++import Control.Monad (unless)+import Data.Aeson (FromJSON, decode)+import Data.ByteString.Lazy.UTF8 (fromString)+import Data.Either (partitionEithers)+import Data.Text (pack, unpack)+import qualified Data.Text.IO as T (getContents)+import Dhall (FromDhall, Generic, Text, ToDhall, auto, input)+import GHC.Generics+import System.Exit (ExitCode (..), die)+import Turtle (empty, parallel, shellStrictWithErr, sort)+import Turtle.Format (printf, (%), s)++data SourceRepoPkg = SourceRepoPkg+    { loc :: Text+    , tag :: Text+    , sub :: [Text]+    }+    deriving (Eq, Ord, Show, Generic, ToDhall, FromDhall)++data Warning+    = NixPrefetchGitFailed Int Text+    | InvalidPrefetchGitOutput Text+    deriving (Eq, Ord, Show)++data NixPrefetchGitOutput = NixPrefetchGitOutput+    { url :: Text+    , rev :: Text+    , sha256 :: Text+    , date :: Text+    }+    deriving (Eq, Ord, Show, Generic, FromJSON)++prefetchSourceRepoPkg :: SourceRepoPkg -> IO (Either Warning NixPrefetchGitOutput)+prefetchSourceRepoPkg SourceRepoPkg{..} = nixPrefetchGit (loc, tag)+{-# INLINE prefetchSourceRepoPkg #-}++nixPrefetchGit :: (Text, Text) -> IO (Either Warning NixPrefetchGitOutput)+nixPrefetchGit (repo, commit) = do+    (exitCode, stdout, stderr) <-+        shellStrictWithErr+            ("nix-prefetch-git " <> repo <> " " <> commit)+            empty+    case exitCode of+        ExitFailure e -> return (Left $ NixPrefetchGitFailed e stderr)+        ExitSuccess ->+            return $+                maybe+                    (Left $ InvalidPrefetchGitOutput stdout)+                    Right+                    (decode (fromString $ unpack stdout))++printUrlTagSha :: NixPrefetchGitOutput -> IO ()+printUrlTagSha NixPrefetchGitOutput{..} =+    printf ("  \""%s%"\".\""%s%"\" = \""%s%"\";\n") url rev sha256++main :: IO ()+main = do+    repos :: [[SourceRepoPkg]] <- input auto =<< T.getContents+    fetches <- sort . parallel $ prefetchSourceRepoPkg <$> concat repos+    let (errs, xs) = partitionEithers fetches+    unless (null errs) $+        die . unlines $ map show errs+    printf "{\n"+    mapM_ printUrlTagSha xs+    printf "}\n"
+ project-nix/sha256map.py view
@@ -0,0 +1,50 @@+#!/usr/bin/env python3+#+# NOTE: Adapted from input-output-hk/iohk-nix, a repository with no license.+# SOURCE: https://github.com/input-output-hk/iohk-nix/blob/master/ci/sha256map-regenerate/sha256map-regenerate.py+#+# This script is intended to be run as+#+#  python3 sha256map.py+#+# And it will iterate over all git references in the given stack project (stack.yaml file)+# and generate a set of sha256 value for them to stdout.++# These can then be imported as:+# haskell-nix.stackProject' { sha256map = import ./sha256map.nix; }+#+# This allows building a project with haskell.nix in a restricted nix setup+# where network access is only permitted if the sha256 of the download is known+# beforehand.++# TODO: accept a cabal.project as input as well.++import re+import subprocess+import json+import sys++# Read stack.yaml from stin+project = sys.stdin.read()++# match looks like this:++#- git: https://github.com/input-output-hk/cardano-base+#  commit: e8a48cf0500b03c744c7fc6f2fedb86e8bdbe055++pattern = r'\- git: (?P<loc>[^ \n]+).*\n' \+        + r'(?P<pad> .*)commit: (?P<tag>[^ \n]+).*\n'++def sha256entry(match):+  dict = match.groupdict()+  prefetchJSON = subprocess.run(+    ["nix-prefetch-git", "--fetch-submodules", "--quiet", dict['loc'], dict['tag']],+    capture_output=True, check=True).stdout+  sha256 = json.loads(prefetchJSON)["sha256"]+  return '"{loc}"."{tag}" = "{sha256}";'.format(**{**dict, **{"sha256": sha256}})++# Write sha256map to stdout+print("{")+for x in sorted([sha256entry(match) for match in re.finditer(pattern, project)]):+  print(" ", x)+print("}")
+ text-templates/cabal/package.dhall view
@@ -0,0 +1,11 @@+{ constraints =+    ../internal/constraint-section.dhall+      ""+      ''+      constraints:+          ''+      "  , "+      (\(c : { dep : Text, ver : Text }) -> "${c.dep} ==${c.ver}")+, packages = ../internal/pkg-section.dhall "" "    " "  , "+, repo-items = ./repo-items.dhall (\(sub : Text) -> "    ${sub}")+}
+ text-templates/cabal/pkg-groups.dhall view
@@ -0,0 +1,20 @@+let TYPES = ./../../types.dhall++in  \(pkg-import : TYPES.CabalRelativity) ->+    \(pkg-groups : List Text) ->+      let null = https://prelude.dhall-lang.org/List/null++      let concatMapSep = https://prelude.dhall-lang.org/Text/concatMapSep++      let import-pkg =+            merge+              { CabalProjectRelative =+                  \(pkg : Text) -> "import: ./project-cabal/pkgs/${pkg}.config"+              , CabalImportRelative =+                  \(pkg : Text) -> "import: ./pkgs/${pkg}.config"+              }+              pkg-import++      in  if    null Text pkg-groups+          then  ""+          else  concatMapSep "\n" Text import-pkg pkg-groups
+ text-templates/cabal/repo-items.dhall view
@@ -0,0 +1,20 @@+let TYPES = ../../types.dhall++in  \(sub-template : Text -> Text) ->+    \(deps-git : List TYPES.SourceRepoPkg) ->+      let T = https://prelude.dhall-lang.org/Text/package.dhall++      in  T.concatMapSep+            "\n"+            TYPES.SourceRepoPkg+            ( \(s : TYPES.SourceRepoPkg) ->+                ''+                source-repository-package+                  type: git+                  location: ${s.loc}+                  tag: ${s.tag}${../internal/sub-items.dhall+                                   "  subdir:"+                                   sub-template+                                   s.sub}''+            )+            deps-git
+ text-templates/dhall2cabal.dhall view
@@ -0,0 +1,86 @@+let TYPES = ../types.dhall++let length = https://prelude.dhall-lang.org/List/length++let show = https://prelude.dhall-lang.org/Natural/show++in  \(stackage-resolver : Text) ->+    \(pkg-set : TYPES.PkgSet) ->+    \ ( pkg-config+      : { constraints : List TYPES.PkgVer+        , source-pkgs :+            { deps-external : List TYPES.SourceRepoPkg+            , deps-internal : List TYPES.SourceRepoPkg+            , forks-external : List TYPES.SourceRepoPkg+            , forks-internal : List TYPES.SourceRepoPkg+            }+        }+      ) ->+      let deps-external = pkg-config.source-pkgs.deps-external++      let deps-internal = pkg-config.source-pkgs.deps-internal++      let forks-external = pkg-config.source-pkgs.forks-external++      let forks-internal = pkg-config.source-pkgs.forks-internal++      let source-deps =+            deps-external # deps-internal # forks-external # forks-internal++      let count =+            \(xs : List TYPES.SourceRepoPkg) ->+              show (length TYPES.SourceRepoPkg xs)++      let countPkgs = \(xs : List Text) -> show (length Text xs)++      let pkgs =+            merge+              { AllPkgs = \(pkgs : List Text) -> pkgs+              , PkgUpgrade = \(pkgs : TYPES.PkgTodoList) -> pkgs.done+              }+              pkg-set++      let pkgs-comment =+            merge+              { AllPkgs =+                  \(pkgs : List Text) ->+                    "-- We have ${countPkgs pkgs} packages."+              , PkgUpgrade =+                  \(pkgs : TYPES.PkgTodoList) ->+                    "-- We have upgraded ${countPkgs+                                             pkgs.done} packages and have ${countPkgs+                                                                              pkgs.todo} yet to do."+              }+              pkg-set++      let cabal = ./cabal/package.dhall++      in  ''+          import: ./project-stackage/${stackage-resolver}.config++          ${pkgs-comment}+          ${cabal.packages pkgs}++          -- We have ${count source-deps} source packages listed in this order:+          --   * external ${count deps-external}+          --   * internal ${count deps-internal}+          --   * external forks ${count forks-external}+          --   * internal forks ${count forks-internal}++          -- Source Packages, external (3rd party).+          ${cabal.repo-items deps-external}++          -- Source Packages, internal to this organisation (private and public).+          ${cabal.repo-items deps-internal}++          -- Source Packages, external (3rd party) forks of other repositories.+          -- Can we help upstream?+          ${cabal.repo-items forks-external}++          -- Source Packages, internal forks of other repositories.+          -- Can we upstream and unfork?+          ${cabal.repo-items forks-internal}++          -- Constraints are equivalent to stack package-version extra dependencies.+          ${cabal.constraints pkg-config.constraints}+          ''
+ text-templates/dhall2config.dhall view
@@ -0,0 +1,15 @@+\(stackage-resolver : Text) ->+\(ghc-version : Text) ->+  ''+  import: ./project-stackage/${stackage-resolver}.config++  import: ./project-cabal/pkgs.config++  import: ./project-cabal/ghc-${ghc-version}/constraints.config+  import: ./project-cabal/ghc-${ghc-version}/deps-external.config+  import: ./project-cabal/ghc-${ghc-version}/deps-internal.config+  import: ./project-cabal/ghc-${ghc-version}/forks-external.config+  import: ./project-cabal/ghc-${ghc-version}/forks-internal.config++  build-info: True+  ''
+ text-templates/dhall2stack.dhall view
@@ -0,0 +1,89 @@+let TYPES = ../types.dhall++let L = https://prelude.dhall-lang.org/List/package.dhall++let show = https://prelude.dhall-lang.org/Natural/show++in  \(stackage-resolver : Text) ->+    \(pkg-set : TYPES.PkgSet) ->+    \ ( pkg-config+      : { constraints : List TYPES.PkgVer+        , source-pkgs :+            { deps-external : List TYPES.SourceRepoPkg+            , deps-internal : List TYPES.SourceRepoPkg+            , forks-external : List TYPES.SourceRepoPkg+            , forks-internal : List TYPES.SourceRepoPkg+            }+        }+      ) ->+      let deps-external = pkg-config.source-pkgs.deps-external++      let deps-internal = pkg-config.source-pkgs.deps-internal++      let forks-external = pkg-config.source-pkgs.forks-external++      let forks-internal = pkg-config.source-pkgs.forks-internal++      let source-deps =+            deps-external # deps-internal # forks-external # forks-internal++      let count =+            \(xs : List TYPES.SourceRepoPkg) ->+              show (L.length TYPES.SourceRepoPkg xs)++      let countPkgs = \(xs : List Text) -> show (L.length Text xs)++      let pkgs =+            merge+              { AllPkgs = \(pkgs : List Text) -> pkgs+              , PkgUpgrade = \(pkgs : TYPES.PkgTodoList) -> pkgs.done+              }+              pkg-set++      let pkgs-comment =+            merge+              { AllPkgs =+                  \(pkgs : List Text) -> "# We have ${countPkgs pkgs} packages."+              , PkgUpgrade =+                  \(pkgs : TYPES.PkgTodoList) ->+                    "# We have upgraded ${countPkgs+                                            pkgs.done} packages and have ${countPkgs+                                                                             pkgs.todo} yet to do."+              }+              pkg-set++      let stack = ./stack/package.dhall++      in      ''+              resolver: ${stackage-resolver}++              ${pkgs-comment}+              ${stack.packages pkgs}+              # We have ${count+                            source-deps} source packages listed in this order:+              #   * external ${count deps-external}+              #   * internal ${count deps-internal}+              #   * external forks ${count forks-external}+              #   * internal forks ${count forks-internal}+              ''+          ++  ( if        L.null TYPES.SourceRepoPkg source-deps+                      &&  L.null TYPES.PkgVer pkg-config.constraints+                then  "extra-deps: []"+                else  ''+                      extra-deps:++                        # Source Packages, external (3rd party).+                      ${stack.repo-items deps-external}+                        # Source Packages, internal to this organisation (private and public).+                      ${stack.repo-items deps-internal}+                        # Source Packages, external (3rd party) forks of other repositories.+                        # Can we help upstream?+                      ${stack.repo-items forks-external}+                        # Source Packages, internal forks of other repositories.+                        # Can we upstream and unfork?+                      ${stack.repo-items forks-internal}+                        # Package versions for published packages either not on Stackage or+                        # not matching the version on Stackage for the resolver we use.+                        # These package-version extra dependencies are equivalent to cabal constraints.+                      ${stack.constraints pkg-config.constraints}''+              )
+ text-templates/internal/constraint-items.dhall view
@@ -0,0 +1,18 @@+let TYPES = ../../types.dhall++in  \(separator : Text) ->+    \(dep-ver-template : TYPES.PkgVer -> Text) ->+    \(xs : List TYPES.PkgVer) ->+      let null = https://prelude.dhall-lang.org/List/null++      let concatMapSep = https://prelude.dhall-lang.org/Text/concatMapSep++      in  if    null TYPES.PkgVer xs+          then  ""+          else  concatMapSep+                  ''++                  ${separator}''+                  TYPES.PkgVer+                  dep-ver-template+                  xs
+ text-templates/internal/constraint-section.dhall view
@@ -0,0 +1,15 @@+let TYPES = ../../types.dhall++in  \(null-content : Text) ->+    \(leader : Text) ->+    \(separator : Text) ->+    \(dep-ver-template : TYPES.PkgVer -> Text) ->+    \(constraints : List TYPES.PkgVer) ->+      let null = https://prelude.dhall-lang.org/List/null++      in  if    null TYPES.PkgVer constraints+          then  null-content+          else  "${leader}${./constraint-items.dhall+                              separator+                              dep-ver-template+                              constraints}"
+ text-templates/internal/pkg-items.dhall view
@@ -0,0 +1,13 @@+\(separator : Text) ->+\(xs : List Text) ->+  let null = https://prelude.dhall-lang.org/List/null++  let concatSep = https://prelude.dhall-lang.org/Text/concatSep++  in  if    null Text xs+      then  ""+      else  concatSep+              ''++              ${separator}''+              xs
+ text-templates/internal/pkg-section.dhall view
@@ -0,0 +1,12 @@+\(null-content : Text) ->+\(first-separator : Text) ->+\(separator : Text) ->+\(pkgs : List Text) ->+  let null = https://prelude.dhall-lang.org/List/null++  in  if    null Text pkgs+      then  null-content+      else  ''+            packages:+            ${first-separator}${./pkg-items.dhall separator pkgs}+            ''
+ text-templates/internal/sub-items.dhall view
@@ -0,0 +1,14 @@+\(sub-field : Text) ->+\(sub-template : Text -> Text) ->+\(xs : List Text) ->+  let L = https://prelude.dhall-lang.org/List/package.dhall++  let T = https://prelude.dhall-lang.org/Text/package.dhall++  in  if    L.null Text xs+      then  ""+      else      ''++                ${sub-field}+                ''+            ++  T.concatMapSep "\n" Text sub-template xs
+ text-templates/stack/package.dhall view
@@ -0,0 +1,9 @@+{ constraints =+    ../internal/constraint-section.dhall+      ""+      "  - "+      "  - "+      (\(c : { dep : Text, ver : Text }) -> "${c.dep}-${c.ver}")+, packages = ../internal/pkg-section.dhall "packages: []" "  - ./" "  - ./"+, repo-items = ./repo-items.dhall (\(sub : Text) -> "      - ${sub}")+}
+ text-templates/stack/repo-items.dhall view
@@ -0,0 +1,22 @@+let TYPES = ../../types.dhall++in  \(sub-template : Text -> Text) ->+    \(deps-git : List TYPES.SourceRepoPkg) ->+      let L = https://prelude.dhall-lang.org/List/package.dhall++      let T = https://prelude.dhall-lang.org/Text/package.dhall++      in  if    L.null TYPES.SourceRepoPkg deps-git+          then  ""+          else  T.concatMap+                  TYPES.SourceRepoPkg+                  ( \(s : TYPES.SourceRepoPkg) ->+                      ''+                        - git: ${s.loc}+                          commit: ${s.tag}${../internal/sub-items.dhall+                                              "    subdirs:"+                                              sub-template+                                              s.sub}+                      ''+                  )+                  deps-git
+ text-templates/yaml2stack/constraints.dhall view
@@ -0,0 +1,13 @@+let TYPES = ../../types.dhall++in  \(xs : List TYPES.PkgVer) ->+      let L = https://prelude.dhall-lang.org/List/package.dhall++      let x = ../stack/package.dhall++      in  if    L.null TYPES.PkgVer xs+          then  "extra-deps: []"+          else  ''+                extra-deps:+                ${x.constraints xs}+                ''
+ text-templates/yaml2stack/pkgs.dhall view
@@ -0,0 +1,1 @@+let x = ../stack/package.dhall in x.packages
+ text-templates/yaml2stack/repo-items.dhall view
@@ -0,0 +1,13 @@+let TYPES = ../../types.dhall++in  \(xs : List TYPES.SourceRepoPkg) ->+      let L = https://prelude.dhall-lang.org/List/package.dhall++      let x = ../stack/package.dhall++      in  if    L.null TYPES.SourceRepoPkg xs+          then  "extra-deps: []"+          else  ''+                extra-deps:+                ${x.repo-items xs}+                ''
+ types.dhall view
@@ -0,0 +1,6 @@+{ PkgTodoList = ./types/PkgTodoList.dhall+, PkgSet = ./types/PkgSet.dhall+, PkgVer = ./types/PkgVer.dhall+, SourceRepoPkg = ./types/SourceRepoPkg.dhall+, CabalRelativity = ./types/CabalRelativity.dhall+}
+ types/CabalRelativity.dhall view
@@ -0,0 +1,1 @@+< CabalProjectRelative | CabalImportRelative >
+ types/PkgSet.dhall view
@@ -0,0 +1,3 @@+let PkgTodoList = ./PkgTodoList.dhall++in  < AllPkgs : List Text | PkgUpgrade : PkgTodoList >
+ types/PkgTodoList.dhall view
@@ -0,0 +1,1 @@+{ done : List Text, todo : List Text }
+ types/PkgVer.dhall view
@@ -0,0 +1,1 @@+{ dep : Text, ver : Text }
+ types/SourceRepoPkg.dhall view
@@ -0,0 +1,1 @@+{ loc : Text, tag : Text, sub : List Text }
+ updo.cabal view
@@ -0,0 +1,151 @@+cabal-version: 1.12++-- This file has been generated from package.dhall by hpack version 0.35.2.+--+-- see: https://github.com/sol/hpack++name:           updo+version:        1.0.0+synopsis:       A style of maintaining and upgrading Haskell projects+description:    From configuration in .dhall, generate projects (stack.yaml and+                cabal.project) and progressively upgrade projects.+category:       Development+homepage:       https://github.com/cabalism/updo#readme+bug-reports:    https://github.com/cabalism/updo/issues+author:         Phil de Joux+maintainer:     phil.dejoux@blockscope.com+copyright:      © 2023 Phil de Joux, © 2023 Block Scope Limited+license:        MPL-2.0+license-file:   LICENSE+build-type:     Simple+tested-with:+    GHC == 8.6.5+  , GHC == 8.10.7+  , GHC == 9.2.8+  , GHC == 9.4.5+extra-source-files:+    ALTERNATIVES.md+    alternatives/cabal2stack/README.md+    alternatives/stack2cabal/README.md+    alternatives/yaml2stack/README.md+    CHANGELOG.md+    HACKING.md+    project-dhall/README.md+    project-dhall2config/README.md+    project-nix/README.md+    README.md+    TEMPLATES.md+    TESTING.md+    package.dhall+    project-dhall/pkgs-upgrade.dhall+    project-nix/project-versions.dhall+    text-templates/cabal/package.dhall+    text-templates/cabal/pkg-groups.dhall+    text-templates/cabal/repo-items.dhall+    text-templates/dhall2cabal.dhall+    text-templates/dhall2config.dhall+    text-templates/dhall2stack.dhall+    text-templates/internal/constraint-items.dhall+    text-templates/internal/constraint-section.dhall+    text-templates/internal/pkg-items.dhall+    text-templates/internal/pkg-section.dhall+    text-templates/internal/sub-items.dhall+    text-templates/stack/package.dhall+    text-templates/stack/repo-items.dhall+    text-templates/yaml2stack/constraints.dhall+    text-templates/yaml2stack/pkgs.dhall+    text-templates/yaml2stack/repo-items.dhall+    types.dhall+    types/CabalRelativity.dhall+    types/PkgSet.dhall+    types/PkgTodoList.dhall+    types/PkgVer.dhall+    types/SourceRepoPkg.dhall+    alternatives/cabal2stack/Makefile+    alternatives/stack2cabal/Makefile+    alternatives/yaml2stack/Makefile+    Makefile+    project-dhall/Makefile+    project-dhall2config/Makefile+    project-nix/Makefile+    project-dhall/pkgs-sorted.hs+    project-dhall/pkgs-upgrade-done.hs+    project-dhall/pkgs-upgrade-partition.hs+    project-dhall2config/pkg-groups.hs+    project-nix/sha256map.hs+    project-nix/sha256map.py+    alternatives/yaml2stack/join-sort-fragments.sh++source-repository head+  type: git+  location: https://github.com/cabalism/updo++executable updo-pkg-groups+  main-is: project-dhall2config/pkg-groups.hs+  other-modules:+      Paths_updo+  build-depends:+      aeson >=2.1.2 && <2.2+    , base >=4.12 && <5+    , dhall >=1.41.1 && <1.42+    , filepath >=1.4.2 && <1.5+    , text >=1.2.3 && <2.1+    , turtle >=1.6.1 && <1.7+    , utf8-string >=1.0.2 && <1.1+  default-language: Haskell2010++executable updo-pkgs-sorted+  main-is: project-dhall/pkgs-sorted.hs+  other-modules:+      Paths_updo+  build-depends:+      aeson >=2.1.2 && <2.2+    , base >=4.12 && <5+    , dhall >=1.41.1 && <1.42+    , filepath >=1.4.2 && <1.5+    , text >=1.2.3 && <2.1+    , turtle >=1.6.1 && <1.7+    , utf8-string >=1.0.2 && <1.1+  default-language: Haskell2010++executable updo-pkgs-upgrade-done+  main-is: project-dhall/pkgs-upgrade-done.hs+  other-modules:+      Paths_updo+  build-depends:+      aeson >=2.1.2 && <2.2+    , base >=4.12 && <5+    , dhall >=1.41.1 && <1.42+    , filepath >=1.4.2 && <1.5+    , text >=1.2.3 && <2.1+    , turtle >=1.6.1 && <1.7+    , utf8-string >=1.0.2 && <1.1+  default-language: Haskell2010++executable updo-pkgs-upgrade-partition+  main-is: project-dhall/pkgs-upgrade-partition.hs+  other-modules:+      Paths_updo+  build-depends:+      aeson >=2.1.2 && <2.2+    , base >=4.12 && <5+    , dhall >=1.41.1 && <1.42+    , filepath >=1.4.2 && <1.5+    , text >=1.2.3 && <2.1+    , turtle >=1.6.1 && <1.7+    , utf8-string >=1.0.2 && <1.1+  default-language: Haskell2010++executable updo-sha256map+  main-is: project-nix/sha256map.hs+  other-modules:+      Paths_updo+  build-depends:+      aeson >=2.1.2 && <2.2+    , base >=4.12 && <5+    , dhall >=1.41.1 && <1.42+    , filepath >=1.4.2 && <1.5+    , text >=1.2.3 && <2.1+    , turtle >=1.6.1 && <1.7+    , utf8-string >=1.0.2 && <1.1+  default-language: Haskell2010