diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..83a6fc0 --- /dev/null +++ b/.envrc @@ -0,0 +1,21 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=" +fi + +use flake . --impure --accept-flake-config + +# Include .env file if it exists locally. Use the .env file to load env vars that you don't want to commit to git +if [ -f .env ] +then + set -o allexport + source .env + set +o allexport +fi +# Add your env vars here +# +# E.g. export AWS_ACCESS_KEY_ID="XXXXX" + +# SMTP config for local development. +export SMTP_HOST="127.0.0.1" # On some computers may need `127.0.1.1` instead. +export SMTP_PORT="1025" +export SMTP_ENCRYPTION="Unencrypted" \ No newline at end of file diff --git a/.ghci b/.ghci new file mode 100644 index 0000000..48ee12a --- /dev/null +++ b/.ghci @@ -0,0 +1,4 @@ +:set -XNoImplicitPrelude +:def loadFromIHP \file -> (System.Environment.getEnv "IHP_LIB") >>= (\ihpLib -> readFile (ihpLib <> "/" <> file)) +:loadFromIHP applicationGhciConfig +import IHP.Prelude \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6f3a3fa --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto eol=lf +*.sql text +*.hs text diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..ae30343 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,95 @@ +name: Test and Deploy + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the main branch + push: + branches: [ main ] + pull_request: + branches: [ main ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + ENV: "qa" + PROJECT_NAME: myproject + SSH_HOST: ${{ secrets.SSH_HOST }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + NIXPKGS: "https://github.com/NixOS/nixpkgs/archive/51bcdc4cdaac48535dabf0ad4642a66774c609ed.tar.gz" + +jobs: + tests: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=${{ env.NIXPKGS}} + + # Use the cachix cache for faster builds. + - name: Cachix Init + uses: cachix/cachix-action@v15 + with: + name: digitallyinduced + skipPush: true + + # Install direnv, which also `direnv allow`s the project. + - uses: HatsuneMiku3939/direnv-action@v1.0.7 + with: + direnvVersion: 2.32.3 + + - name: Run project and tests + run: | + # @see https://github.com/actions/runner-images/issues/2840#issuecomment-1284059930 + # IHP - NixOS requires a lots of disk space. + # Larger projects could easily run into unexpected failures. + sudo rm -rf /usr/share/dotnet + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + + # Build generated files. + nix-shell --run "make build/Generated/Types.hs" + + # Start the project in the background. + nix-shell --run "devenv up &" + + # Execute the tests. + nix-shell --run "runghc $(make print-ghc-extensions) -i. -ibuild -iConfig Test/Main.hs" + + deploy: + name: Deploy + needs: tests + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup SSH + run: | + mkdir -p ~/.ssh + echo "${{ env.SSH_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H ${{ env.SSH_HOST }} >> ~/.ssh/known_hosts + echo -e "Host ${{ env.PROJECT_NAME}}-${{ env.ENV }}\n HostName ${{ env.SSH_HOST }}\n User ${{ env.SSH_USER }}\n IdentityFile ~/.ssh/id_rsa" > ~/.ssh/config + chmod 600 ~/.ssh/config + + - uses: cachix/install-nix-action@v20 + with: + nix_path: nixpkgs=${{ env.NIXPKGS}} + + - name: Cachix Init + uses: cachix/cachix-action@v12 + with: + name: digitallyinduced + skipPush: true + + - uses: HatsuneMiku3939/direnv-action@v1 + with: + direnvVersion: 2.32.3 + + - name: Deploy + run: | + deploy-to-nixos ${{ env.PROJECT_NAME}}-${{ env.ENV }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c63b6ae --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +.DS_Store +.idea +.env +tmp +result +node_modules +bin +*.iml +Generated +dist +dist-newstyle +*.dyn_hi +*.dyn_o +*.hi +*.o +build +gen +del +static/prod.* +Config/client_session_key.aes + +# Ignore locally checked out IHP version +IHP + +# devenv.sh +.devenv* +devenv.local.nix +.direnv diff --git a/.stylish-haskell.yaml b/.stylish-haskell.yaml new file mode 100644 index 0000000..b803c01 --- /dev/null +++ b/.stylish-haskell.yaml @@ -0,0 +1,415 @@ +# stylish-haskell configuration file +# ================================== + +# The stylish-haskell tool is mainly configured by specifying steps. These steps +# are a list, so they have an order, and one specific step may appear more than +# once (if needed). Each file is processed by these steps in the given order. +steps: + # Convert some ASCII sequences to their Unicode equivalents. This is disabled + # by default. + # - unicode_syntax: + # # In order to make this work, we also need to insert the UnicodeSyntax + # # language pragma. If this flag is set to true, we insert it when it's + # # not already present. You may want to disable it if you configure + # # language extensions using some other method than pragmas. Default: + # # true. + # add_language_pragma: true + + # Format module header + # + # Currently, this option is not configurable and will format all exports and + # module declarations to minimize diffs + # + # - module_header: + # # How many spaces use for indentation in the module header. + # indent: 4 + # + # # Should export lists be sorted? Sorting is only performed within the + # # export section, as delineated by Haddock comments. + # sort: true + # + # # See `separate_lists` for the `imports` step. + # separate_lists: true + # + # # When to break the "where". + # # Possible values: + # # - exports: only break when there is an explicit export list. + # # - single: only break when the export list counts more than one export. + # # - inline: only break when the export list is too long. This is + # # determined by the `columns` setting. Not applicable when the export + # # list contains comments as newlines will be required. + # # - always: always break before the "where". + # break_where: exports + # + # # Where to put open bracket + # # Possible values: + # # - same_line: put open bracket on the same line as the module name, before the + # # comment of the module + # # - next_line: put open bracket on the next line, after module comment + # open_bracket: next_line + + # Format record definitions. This is disabled by default. + # + # You can control the layout of record fields. The only rules that can't be configured + # are these: + # + # - "|" is always aligned with "=" + # - "," in fields is always aligned with "{" + # - "}" is likewise always aligned with "{" + # + # - records: + # # How to format equals sign between type constructor and data constructor. + # # Possible values: + # # - "same_line" -- leave "=" AND data constructor on the same line as the type constructor. + # # - "indent N" -- insert a new line and N spaces from the beginning of the next line. + # equals: "indent 2" + # + # # How to format first field of each record constructor. + # # Possible values: + # # - "same_line" -- "{" and first field goes on the same line as the data constructor. + # # - "indent N" -- insert a new line and N spaces from the beginning of the data constructor + # first_field: "indent 2" + # + # # How many spaces to insert between the column with "," and the beginning of the comment in the next line. + # field_comment: 2 + # + # # How many spaces to insert before "deriving" clause. Deriving clauses are always on separate lines. + # deriving: 2 + # + # # How many spaces to insert before "via" clause counted from indentation of deriving clause + # # Possible values: + # # - "same_line" -- "via" part goes on the same line as "deriving" keyword. + # # - "indent N" -- insert a new line and N spaces from the beginning of "deriving" keyword. + # via: "indent 2" + # + # # Sort typeclass names in the "deriving" list alphabetically. + # sort_deriving: true + # + # # Whether or not to break enums onto several lines + # # + # # Default: false + # break_enums: false + # + # # Whether or not to break single constructor data types before `=` sign + # # + # # Default: true + # break_single_constructors: true + # + # # Whether or not to curry constraints on function. + # # + # # E.g: @allValues :: Enum a => Bounded a => Proxy a -> [a]@ + # # + # # Instead of @allValues :: (Enum a, Bounded a) => Proxy a -> [a]@ + # # + # # Default: false + # curried_context: false + + # Align the right hand side of some elements. This is quite conservative + # and only applies to statements where each element occupies a single + # line. + # Possible values: + # - always - Always align statements. + # - adjacent - Align statements that are on adjacent lines in groups. + # - never - Never align statements. + # All default to always. + - simple_align: + cases: always + top_level_patterns: always + records: always + multi_way_if: always + + # Import cleanup + - imports: + # There are different ways we can align names and lists. + # + # - global: Align the import names and import list throughout the entire + # file. + # + # - file: Like global, but don't add padding when there are no qualified + # imports in the file. + # + # - group: Only align the imports per group (a group is formed by adjacent + # import lines). + # + # - none: Do not perform any alignment. + # + # Default: global. + align: none + + # The following options affect only import list alignment. + # + # List align has following options: + # + # - after_alias: Import list is aligned with end of import including + # 'as' and 'hiding' keywords. + # + # > import qualified Data.List as List (concat, foldl, foldr, head, + # > init, last, length) + # + # - with_alias: Import list is aligned with start of alias or hiding. + # + # > import qualified Data.List as List (concat, foldl, foldr, head, + # > init, last, length) + # + # - with_module_name: Import list is aligned `list_padding` spaces after + # the module name. + # + # > import qualified Data.List as List (concat, foldl, foldr, head, + # init, last, length) + # + # This is mainly intended for use with `pad_module_names: false`. + # + # > import qualified Data.List as List (concat, foldl, foldr, head, + # init, last, length, scanl, scanr, take, drop, + # sort, nub) + # + # - new_line: Import list starts always on new line. + # + # > import qualified Data.List as List + # > (concat, foldl, foldr, head, init, last, length) + # + # - repeat: Repeat the module name to align the import list. + # + # > import qualified Data.List as List (concat, foldl, foldr, head) + # > import qualified Data.List as List (init, last, length) + # + # Default: after_alias + list_align: after_alias + + # Right-pad the module names to align imports in a group: + # + # - true: a little more readable + # + # > import qualified Data.List as List (concat, foldl, foldr, + # > init, last, length) + # > import qualified Data.List.Extra as List (concat, foldl, foldr, + # > init, last, length) + # + # - false: diff-safe + # + # > import qualified Data.List as List (concat, foldl, foldr, init, + # > last, length) + # > import qualified Data.List.Extra as List (concat, foldl, foldr, + # > init, last, length) + # + # Default: true + pad_module_names: true + + # Long list align style takes effect when import is too long. This is + # determined by 'columns' setting. + # + # - inline: This option will put as much specs on same line as possible. + # + # - new_line: Import list will start on new line. + # + # - new_line_multiline: Import list will start on new line when it's + # short enough to fit to single line. Otherwise it'll be multiline. + # + # - multiline: One line per import list entry. + # Type with constructor list acts like single import. + # + # > import qualified Data.Map as M + # > ( empty + # > , singleton + # > , ... + # > , delete + # > ) + # + # Default: inline + long_list_align: inline + + # Align empty list (importing instances) + # + # Empty list align has following options + # + # - inherit: inherit list_align setting + # + # - right_after: () is right after the module name: + # + # > import Vector.Instances () + # + # Default: inherit + empty_list_align: inherit + + # List padding determines indentation of import list on lines after import. + # This option affects 'long_list_align'. + # + # - : constant value + # + # - module_name: align under start of module name. + # Useful for 'file' and 'group' align settings. + # + # Default: 4 + list_padding: 4 + + # Separate lists option affects formatting of import list for type + # or class. The only difference is single space between type and list + # of constructors, selectors and class functions. + # + # - true: There is single space between Foldable type and list of it's + # functions. + # + # > import Data.Foldable (Foldable (fold, foldl, foldMap)) + # + # - false: There is no space between Foldable type and list of it's + # functions. + # + # > import Data.Foldable (Foldable(fold, foldl, foldMap)) + # + # Default: true + separate_lists: true + + # Space surround option affects formatting of import lists on a single + # line. The only difference is single space after the initial + # parenthesis and a single space before the terminal parenthesis. + # + # - true: There is single space associated with the enclosing + # parenthesis. + # + # > import Data.Foo ( foo ) + # + # - false: There is no space associated with the enclosing parenthesis + # + # > import Data.Foo (foo) + # + # Default: false + space_surround: false + + # Post qualify option moves any qualifies found in import declarations + # to the end of the declaration. This also adjust padding for any + # unqualified import declarations. + # + # - true: Qualified as is moved to the end of the + # declaration. + # + # > import Data.Bar + # > import Data.Foo qualified as F + # + # - false: Qualified remains in the default location and unqualified + # imports are padded to align with qualified imports. + # + # > import Data.Bar + # > import qualified Data.Foo as F + # + # Default: false + post_qualify: false + + # Language pragmas + - language_pragmas: + # We can generate different styles of language pragma lists. + # + # - vertical: Vertical-spaced language pragmas, one per line. + # + # - compact: A more compact style. + # + # - compact_line: Similar to compact, but wrap each line with + # `{-# LANGUAGE #-}'. + # + # - vertical_compact: Similar to vertical, but use only one language pragma. + # + # Default: vertical. + style: vertical + + # Align affects alignment of closing pragma brackets. + # + # - true: Brackets are aligned in same column. + # + # - false: Brackets are not aligned together. There is only one space + # between actual import and closing bracket. + # + # Default: true + align: true + + # stylish-haskell can detect redundancy of some language pragmas. If this + # is set to true, it will remove those redundant pragmas. Default: true. + remove_redundant: true + + # Language prefix to be used for pragma declaration, this allows you to + # use other options non case-sensitive like "language" or "Language". + # If a non correct String is provided, it will default to: LANGUAGE. + language_prefix: LANGUAGE + + # Replace tabs by spaces. This is disabled by default. + # - tabs: + # # Number of spaces to use for each tab. Default: 8, as specified by the + # # Haskell report. + # spaces: 8 + + # Remove trailing whitespace + - trailing_whitespace: {} + + # Squash multiple spaces between the left and right hand sides of some + # elements into single spaces. Basically, this undoes the effect of + # simple_align but is a bit less conservative. + # - squash: {} + +# A common setting is the number of columns (parts of) code will be wrapped +# to. Different steps take this into account. +# +# Set this to null to disable all line wrapping. +# +# Default: 80. +columns: 80 + +# By default, line endings are converted according to the OS. You can override +# preferred format here. +# +# - native: Native newline format. CRLF on Windows, LF on other OSes. +# +# - lf: Convert to LF ("\n"). +# +# - crlf: Convert to CRLF ("\r\n"). +# +# Default: native. +newline: native + +# Sometimes, language extensions are specified in a cabal file or from the +# command line instead of using language pragmas in the file. stylish-haskell +# needs to be aware of these, so it can parse the file correctly. +# +# No language extensions are enabled by default. +language_extensions: + - BangPatterns + - BlockArguments + - BlockArguments + - DataKinds + - DefaultSignatures + - DeriveDataTypeable + - DeriveGeneric + - DerivingVia + - DisambiguateRecordFields + - DuplicateRecordFields + - EmptyDataDeriving + - FlexibleContexts + - FlexibleInstances + - FunctionalDependencies + - ImplicitParams + - InstanceSigs + - LambdaCase + - MultiParamTypeClasses + - MultiWayIf + - MultiWayIf + - NamedFieldPuns + - NoImplicitPrelude + - OverloadedLabels + - OverloadedStrings + - PackageImports + - PartialTypeSignatures + - PartialTypeSignatures + - QuasiQuotes + - Rank2Types + - RecordWildCards + - ScopedTypeVariables + - StandaloneDeriving + - TemplateHaskell + - TypeApplications + - TypeFamilies + - TypeOperators + - TypeSynonymInstances + - UndecidableInstances + +# Attempt to find the cabal file in ancestors of the current directory, and +# parse options (currently only language extensions) from that. +# +# Default: true +cabal: true diff --git a/App.cabal b/App.cabal new file mode 100644 index 0000000..9463cfc --- /dev/null +++ b/App.cabal @@ -0,0 +1,71 @@ +-- This file is only a stub file, please see default.nix for adding dependencies. +-- +-- Learn more about dependency management in IHP: https://ihp.digitallyinduced.com/Guide/package-management.html +-- +-- If you're looking at this file because you're trying to integrate a HLS or other tooling with your IHP project, check out the documentation on Editors and Tooling: https://ihp.digitallyinduced.com/Guide/editors.html +-- +-- This cabal file is inside your project as some haskell tools only work when there's a cabal file. It's not actually used for anything besides providing support for haskell tools. +-- + +name: App +version: 0.1.0.0 +-- synopsis: +-- description: +license: AllRightsReserved +license-file: LICENSE +author: Developers +maintainer: developers@example.com +-- copyright: +-- category: +build-type: Simple +cabal-version: >=1.10 + +executable App + main-is: Main.hs + -- other-modules: + -- other-extensions: + build-depends: + ihp, + base, + wai, + text + hs-source-dirs: . + default-language: Haskell2010 + extensions: + OverloadedStrings + , NoImplicitPrelude + , ImplicitParams + , Rank2Types + , DisambiguateRecordFields + , NamedFieldPuns + , DuplicateRecordFields + , OverloadedLabels + , FlexibleContexts + , TypeSynonymInstances + , FlexibleInstances + , QuasiQuotes + , TypeFamilies + , PackageImports + , ScopedTypeVariables + , RecordWildCards + , TypeApplications + , DataKinds + , InstanceSigs + , DeriveGeneric + , MultiParamTypeClasses + , TypeOperators + , DeriveDataTypeable + , MultiWayIf + , UndecidableInstances + , BlockArguments + , PartialTypeSignatures + , LambdaCase + , DefaultSignatures + , EmptyDataDeriving + , BangPatterns + , BlockArguments + , MultiWayIf + , FunctionalDependencies + , PartialTypeSignatures + , StandaloneDeriving + , DerivingVia \ No newline at end of file diff --git a/Application/Fixtures.sql b/Application/Fixtures.sql new file mode 100644 index 0000000..e69de29 diff --git a/Application/Helper/Controller.hs b/Application/Helper/Controller.hs new file mode 100644 index 0000000..3aa7c2a --- /dev/null +++ b/Application/Helper/Controller.hs @@ -0,0 +1,5 @@ +module Application.Helper.Controller where + +import IHP.ControllerPrelude + +-- Here you can add functions which are available in all your controllers \ No newline at end of file diff --git a/Application/Helper/View.hs b/Application/Helper/View.hs new file mode 100644 index 0000000..ce8ac64 --- /dev/null +++ b/Application/Helper/View.hs @@ -0,0 +1,5 @@ +module Application.Helper.View where + +import IHP.ViewPrelude + +-- Here you can add functions which are available in all your views \ No newline at end of file diff --git a/Application/Schema.sql b/Application/Schema.sql new file mode 100644 index 0000000..b743c66 --- /dev/null +++ b/Application/Schema.sql @@ -0,0 +1 @@ +-- Your database schema. Use the Schema Designer at http://localhost:8001/ to add some tables. diff --git a/Application/Script/Prelude.hs b/Application/Script/Prelude.hs new file mode 100644 index 0000000..66feaec --- /dev/null +++ b/Application/Script/Prelude.hs @@ -0,0 +1,12 @@ +module Application.Script.Prelude +( module IHP.ControllerPrelude +, module Generated.Types +, module IHP.Prelude +, module IHP.ScriptSupport +) +where + +import IHP.Prelude +import IHP.ControllerPrelude +import Generated.Types +import IHP.ScriptSupport \ No newline at end of file diff --git a/Config/Config.hs b/Config/Config.hs new file mode 100644 index 0000000..7284574 --- /dev/null +++ b/Config/Config.hs @@ -0,0 +1,11 @@ +module Config where + +import IHP.Prelude +import IHP.Environment +import IHP.FrameworkConfig + +config :: ConfigBuilder +config = do + -- See https://ihp.digitallyinduced.com/Guide/config.html + -- for what you can do here + pure () \ No newline at end of file diff --git a/Config/nix/haskell-packages/.keep b/Config/nix/haskell-packages/.keep new file mode 100644 index 0000000..e69de29 diff --git a/Config/nix/nixpkgs-config.nix b/Config/nix/nixpkgs-config.nix new file mode 100644 index 0000000..fc530de --- /dev/null +++ b/Config/nix/nixpkgs-config.nix @@ -0,0 +1,7 @@ +# See https://ihp.digitallyinduced.com/Guide/package-management.html +{ ihp, additionalNixpkgsOptions, ... }: +import "${toString ihp}/NixSupport/make-nixpkgs-from-options.nix" { + ihp = ihp; + haskellPackagesDir = ./haskell-packages/.; + additionalNixpkgsOptions = additionalNixpkgsOptions; +} \ No newline at end of file diff --git a/Main.hs b/Main.hs new file mode 100644 index 0000000..81d4d95 --- /dev/null +++ b/Main.hs @@ -0,0 +1,17 @@ +module Main where +import IHP.Prelude + +import Config +import qualified IHP.Server +import IHP.RouterSupport +import IHP.FrameworkConfig +import IHP.Job.Types + +instance FrontController RootApplication where + controllers = [] + +instance Worker RootApplication where + workers _ = [] + +main :: IO () +main = IHP.Server.run config diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..96fd66b --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +CSS_FILES += ${IHP}/static/vendor/bootstrap.min.css +CSS_FILES += ${IHP}/static/vendor/flatpickr.min.css +CSS_FILES += static/app.css + +JS_FILES += ${IHP}/static/vendor/jquery-3.6.0.slim.min.js +JS_FILES += ${IHP}/static/vendor/timeago.js +JS_FILES += ${IHP}/static/vendor/popper.min.js +JS_FILES += ${IHP}/static/vendor/bootstrap.min.js +JS_FILES += ${IHP}/static/vendor/flatpickr.js +JS_FILES += ${IHP}/static/helpers.js +JS_FILES += ${IHP}/static/vendor/morphdom-umd.min.js +JS_FILES += ${IHP}/static/vendor/turbolinks.js +JS_FILES += ${IHP}/static/vendor/turbolinksInstantClick.js +JS_FILES += ${IHP}/static/vendor/turbolinksMorphdom.js + +include ${IHP}/Makefile.dist + diff --git a/README.md b/README.md index e46f50d..9930bcc 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,71 @@ -# GitHub Codespaces IHP Template +# IHP Project -This is an IHP template configured to run on GitHub Codespaces and [VSCode Devcontainers](https://code.visualstudio.com/docs/devcontainers/containers). +This is an IHP (Integrated Haskell Platform) project with GitHub Actions for testing and deployment. For more information about IHP, see the [IHP Documentation](https://ihp.digitallyinduced.com/Guide/). -## Getting Started +## GitHub Actions Workflow -### For New Projects -1. Create a repository from this template. -2. Run it in Codespaces / Devcontainers. -3. Wait for the initial setup to complete. This will take a few minutes and will use two bash windows, one of which will close when it's done. The other should be a blank terminal when it's done. -4. Run `devenv up` to start the server. -5. Have fun with IHP! :) +This project includes a GitHub Actions workflow for automated testing and deployment. The workflow is defined in `.github/workflows/test.yml`. -### An existing IHP project -To add support to an existing IHP project, simply copy the [devcontainer configuration](.devcontainer/devcontainer.json) to your project, -placing it in `.devcontainer/devcontainer.json`. Then follow the above instructions. +### Workflow Triggers -## Note -Sometimes GitHub updates Codespaces or their base container image, which may break this devcontainer configuration. Please check here regularly for -updates and post an issue if you have problems running a Codespace / Devcontainer. To update, simply copy the new `devcontainer.json` -to your project, and then rebuild the container or recreate your Codespace / Devcontainer entirely. +The workflow is triggered on: +- Push to the `main` branch +- Pull requests to the `main` branch +- Manual trigger from the GitHub Actions tab -See also [ihp.digitallyinduced.com](https://ihp.digitallyinduced.com/) +### Testing + +The testing job performs the following steps: +1. Checks out the code +2. Sets up Nix +3. Initializes Cachix for faster builds +4. Installs and allows direnv +5. Builds generated files +6. Starts the project in the background +7. Runs the tests + +### Deployment + +For deployment, follow the [IHP Deployment Guide](https://ihp.digitallyinduced.com/Guide/deployment.html#deploying-with-deploytonixos) to set up a proper NixOS server for your project. + +The deployment job runs after successful tests and only for the `main` branch. It performs the following steps: +1. Checks out the code +2. Sets up SSH for deployment +3. Sets up Nix +4. Initializes Cachix +5. Sets up direnv +6. Deploys to a NixOS server + +## Setup Instructions + +To use the GitHub Actions workflow in this project: + +1. Set up the following secrets in your GitHub [repository settings](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions): + - `SSH_HOST`: The hostname or IP address of your deployment server + - `SSH_USER`: The username for SSH access to the deployment server + - `SSH_PRIVATE_KEY`: The private SSH key for authentication + +2. Modify the `env` section in `.github/workflows/test.yml` if needed: + - Update `PROJECT_NAME` to match your project + - Adjust `ENV` if you want to use a different environment name + - Update `NIXPKGS` if you want to use a different Nixpkgs version + +3. Ensure your project has the necessary test files in the `Test` directory. + +4. If your deployment process differs, modify the `deploy` job in the workflow file accordingly. + +5. Push your changes to the `main` branch to trigger the workflow. + +## Manual Workflow Trigger + +You can manually trigger the workflow from the Actions tab in your GitHub repository. This is useful for running tests or deploying without pushing changes. + +## Customization + +Feel free to customize the workflow file to fit your specific project needs. You may want to add additional steps, change the deployment process, or modify the testing procedure. + +## Support + +For issues related to IHP or this project's setup, please refer to the [IHP documentation](https://ihp.digitallyinduced.com/Guide/) or seek help on the [IHP Forum](https://ihp.digitallyinduced.com/community/). + +For project-specific issues, please open an issue in this repository. \ No newline at end of file diff --git a/Setup.hs b/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..8bd1d4a --- /dev/null +++ b/default.nix @@ -0,0 +1,9 @@ +# For backwards compatibility using flake.nix +(import + ( + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9.tar.gz"; + sha256 = "sha256:1prd9b1xx8c0sfwnyzkspplh30m613j42l1k789s521f4kv4c2z2"; + } + ) +{ src = ./.; }).defaultNix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..af25d21 --- /dev/null +++ b/flake.lock @@ -0,0 +1,1246 @@ +{ + "nodes": { + "cachix": { + "inputs": { + "devenv": "devenv_2", + "flake-compat": [ + "ihp", + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "ihp", + "devenv", + "nixpkgs" + ], + "pre-commit-hooks": [ + "ihp", + "devenv", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1712055811, + "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", + "owner": "cachix", + "repo": "cachix", + "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat_2", + "nix": "nix_2", + "nixpkgs": [ + "ihp", + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1714390914, + "narHash": "sha256-W5DFIifCjGYJXJzLU3RpqBeqes4zrf0Sr/6rwzTygPU=", + "owner": "cachix", + "repo": "devenv", + "rev": "34e6461fd76b5f51ad5f8214f5cf22c4cd7a196e", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "refs/tags/v1.0.5", + "repo": "devenv", + "type": "github" + } + }, + "devenv_2": { + "inputs": { + "flake-compat": [ + "ihp", + "devenv", + "cachix", + "flake-compat" + ], + "nix": "nix", + "nixpkgs": "nixpkgs", + "poetry2nix": "poetry2nix", + "pre-commit-hooks": [ + "ihp", + "devenv", + "cachix", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1708704632, + "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", + "owner": "cachix", + "repo": "devenv", + "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "python-rewrite", + "repo": "devenv", + "type": "github" + } + }, + "devenv_3": { + "inputs": { + "flake-compat": "flake-compat_3", + "nix": "nix_3", + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks_2" + }, + "locked": { + "lastModified": 1694422554, + "narHash": "sha256-s5NTPzT66yIMmau+ZGP7q9z4NjgceDETL4xZ6HJ/TBg=", + "owner": "cachix", + "repo": "devenv", + "rev": "63d20fe09aa09060ea9ec9bb6d582c025402ba15", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_4": { + "inputs": { + "flake-compat": "flake-compat_4", + "nix": "nix_4", + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "ihp-boilerplate", + "ihp", + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks_3" + }, + "locked": { + "lastModified": 1686054274, + "narHash": "sha256-93aebyN7EMmeFFXisFIvp28UEbrozu79vd3pKPjvNR0=", + "owner": "cachix", + "repo": "devenv", + "rev": "c51a56bac8853c019241fe8d821c0a0d82422835", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_3": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_4": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "ihp", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1714641030, + "narHash": "sha256-yzcRNDoyVP7+SCNX0wmuDju1NUCt8Dz9+lyUXEI0dbI=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "e5d10a24b66c3ea8f150e47dfdb0416ab7c3390e", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1693611461, + "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_3": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_2" + }, + "locked": { + "lastModified": 1685662779, + "narHash": "sha256-cKDDciXGpMEjP1n6HlzKinN0H+oLmNpgeCTzYnsA2po=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "71fb97f0d875fd4de4994dfb849f2c75e17eb6c3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_4": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "ihp", + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gitignore_2": { + "inputs": { + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gitignore_3": { + "inputs": { + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "ihp-boilerplate", + "ihp", + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "ihp": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts", + "ihp-boilerplate": "ihp-boilerplate", + "nix-filter": "nix-filter_2", + "nixpkgs": "nixpkgs_4", + "systems": "systems_6" + }, + "locked": { + "lastModified": 1714870134, + "narHash": "sha256-DmaIr9kF+TG24wVNPVufxC74TYMCLziLYS9hCZHBDTc=", + "owner": "digitallyinduced", + "repo": "ihp", + "rev": "29436fd63f11ccad9b10168bba7d14df737ee287", + "type": "github" + }, + "original": { + "owner": "digitallyinduced", + "ref": "v1.3", + "repo": "ihp", + "type": "github" + } + }, + "ihp-boilerplate": { + "inputs": { + "devenv": [ + "ihp", + "ihp-boilerplate", + "ihp", + "devenv" + ], + "flake-parts": [ + "ihp", + "ihp-boilerplate", + "ihp", + "flake-parts" + ], + "ihp": "ihp_2", + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "nixpkgs" + ], + "systems": [ + "ihp", + "ihp-boilerplate", + "ihp", + "systems" + ] + }, + "locked": { + "lastModified": 1710175252, + "narHash": "sha256-QIFqo64U69uUGJ7pgBr37T3yAKK0n1ueqagKmnm+XWw=", + "owner": "digitallyinduced", + "repo": "ihp-boilerplate", + "rev": "323591d6135f7a89b5b4e518d5d420cd5b046fe2", + "type": "github" + }, + "original": { + "owner": "digitallyinduced", + "repo": "ihp-boilerplate", + "type": "github" + } + }, + "ihp-boilerplate_2": { + "inputs": { + "devenv": [ + "ihp", + "ihp-boilerplate", + "ihp", + "ihp-boilerplate", + "ihp", + "devenv" + ], + "flake-parts": [ + "ihp", + "ihp-boilerplate", + "ihp", + "ihp-boilerplate", + "ihp", + "flake-parts" + ], + "ihp": "ihp_3", + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "ihp-boilerplate", + "ihp", + "nixpkgs" + ], + "systems": [ + "ihp", + "ihp-boilerplate", + "ihp", + "ihp-boilerplate", + "ihp", + "systems" + ] + }, + "locked": { + "lastModified": 1689954789, + "narHash": "sha256-RsgD1YGSlx+K/GkTspOdg/tz47PyZZDc66PzfFZvqBk=", + "owner": "digitallyinduced", + "repo": "ihp-boilerplate", + "rev": "832d1a5aed4dc3625486c82b06a1d07024267680", + "type": "github" + }, + "original": { + "owner": "digitallyinduced", + "repo": "ihp-boilerplate", + "type": "github" + } + }, + "ihp-boilerplate_3": { + "flake": false, + "locked": { + "lastModified": 1686165507, + "narHash": "sha256-ZaP8GfqjZDnMayPcvWxEqnZmRs4ixf5O5d1Ba867m4c=", + "owner": "digitallyinduced", + "repo": "ihp-boilerplate", + "rev": "ff63ce46b6fb68f1b8b3cdb0bdd6749f7ef1df93", + "type": "github" + }, + "original": { + "owner": "digitallyinduced", + "ref": "nicolas/flake", + "repo": "ihp-boilerplate", + "type": "github" + } + }, + "ihp_2": { + "inputs": { + "devenv": "devenv_3", + "flake-parts": "flake-parts_2", + "ihp-boilerplate": "ihp-boilerplate_2", + "nix-filter": "nix-filter", + "nixpkgs": "nixpkgs_3", + "systems": "systems_5" + }, + "locked": { + "lastModified": 1700013490, + "narHash": "sha256-oQz7ZBrHe6WwYMwnxxUgnYM55CuH5Oxjz6mrLnYbB7U=", + "owner": "digitallyinduced", + "repo": "ihp", + "rev": "d59a65d71943cb506eee3ad6255f017963237359", + "type": "github" + }, + "original": { + "owner": "digitallyinduced", + "ref": "v1.2", + "repo": "ihp", + "type": "github" + } + }, + "ihp_3": { + "inputs": { + "devenv": "devenv_4", + "flake-parts": "flake-parts_3", + "ihp-boilerplate": "ihp-boilerplate_3", + "nixpkgs": "nixpkgs_2", + "systems": "systems_4" + }, + "locked": { + "lastModified": 1689949405, + "narHash": "sha256-o0ZSDaDFgwbXqozHfcXKxW4FeF7JqaGprAh6r7NhvhE=", + "owner": "digitallyinduced", + "repo": "ihp", + "rev": "e6c6eaf1d089423a03e586cd25d1eda39f5a6b11", + "type": "github" + }, + "original": { + "owner": "digitallyinduced", + "ref": "v1.1", + "repo": "ihp", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "lowdown-src_2": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "ihp", + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix-filter": { + "locked": { + "lastModified": 1694434370, + "narHash": "sha256-7yfdTR4mCvWZ39Q6HUcsa18tr0mg+fJZSaHE/63rwoo=", + "owner": "numtide", + "repo": "nix-filter", + "rev": "d6381c442f79f2f1fdfde00521c3d15d6c21218e", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "nix-filter", + "type": "github" + } + }, + "nix-filter_2": { + "locked": { + "lastModified": 1710156097, + "narHash": "sha256-1Wvk8UP7PXdf8bCCaEoMnOT1qe5/Duqgj+rL8sRQsSM=", + "owner": "numtide", + "repo": "nix-filter", + "rev": "3342559a24e85fc164b295c3444e8a139924675b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "nix-filter", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "ihp", + "devenv", + "cachix", + "devenv", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix_2": { + "inputs": { + "flake-compat": [ + "ihp", + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "ihp", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression_2" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix_3": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression_3" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nix_4": { + "inputs": { + "lowdown-src": "lowdown-src_2", + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "ihp-boilerplate", + "ihp", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression_4" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1692808169, + "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1693471703, + "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib_2": { + "locked": { + "dir": "lib", + "lastModified": 1685564631, + "narHash": "sha256-8ywr3AkblY4++3lIVxmrWZFzac7+f32ZEhH/A8pNscI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "4f53efe34b3a8877ac923b9350c874e3dcd5dc0a", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_2": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_3": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_4": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable_2": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable_3": { + "locked": { + "lastModified": 1678872516, + "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1681488673, + "narHash": "sha256-PmojOyePBNvbY3snYE7NAQHTLB53t7Ro+pgiJ4wPCuk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a95ed9fe764c3ba2bf2d2fa223012c379cd6b32e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a95ed9fe764c3ba2bf2d2fa223012c379cd6b32e", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1696291921, + "narHash": "sha256-isKgVAoUxuxYEuO3Q4xhbfKcZrF/+UkJtOTv0eb/W5E=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ea0284a3da391822909be5e98a60c1e62572a7dc", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ea0284a3da391822909be5e98a60c1e62572a7dc", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1714864423, + "narHash": "sha256-Wx3Y6arRJD1pd3c8SnD7dfW7KWuCr/r248P/5XLaMdM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "54b4bb956f9891b872904abdb632cea85a033ff2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "ihp", + "devenv", + "cachix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692876271, + "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "ihp", + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils_2", + "gitignore": "gitignore", + "nixpkgs": [ + "ihp", + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1713775815, + "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_2": { + "inputs": { + "flake-compat": [ + "ihp", + "ihp-boilerplate", + "ihp", + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils_3", + "gitignore": "gitignore_2", + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable_2" + }, + "locked": { + "lastModified": 1688056373, + "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_3": { + "inputs": { + "flake-compat": [ + "ihp", + "ihp-boilerplate", + "ihp", + "ihp-boilerplate", + "ihp", + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils_4", + "gitignore": "gitignore_3", + "nixpkgs": [ + "ihp", + "ihp-boilerplate", + "ihp", + "ihp-boilerplate", + "ihp", + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable_3" + }, + "locked": { + "lastModified": 1682596858, + "narHash": "sha256-Hf9XVpqaGqe/4oDGr30W8HlsWvJXtMsEPHDqHZA6dDg=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "fb58866e20af98779017134319b5663b8215d912", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": [ + "ihp", + "devenv" + ], + "flake-parts": [ + "ihp", + "flake-parts" + ], + "ihp": "ihp", + "nixpkgs": [ + "ihp", + "nixpkgs" + ], + "systems": [ + "ihp", + "systems" + ] + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_4": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_5": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_6": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..624da5f --- /dev/null +++ b/flake.nix @@ -0,0 +1,146 @@ +{ + inputs = { + ihp.url = "github:digitallyinduced/ihp/v1.3"; + nixpkgs.follows = "ihp/nixpkgs"; + flake-parts.follows = "ihp/flake-parts"; + devenv.follows = "ihp/devenv"; + systems.follows = "ihp/systems"; + }; + + outputs = inputs@{ self, nixpkgs, ihp, flake-parts, systems, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + + systems = import systems; + imports = [ ihp.flakeModules.default ]; + + perSystem = { pkgs, ... }: { + ihp = { + # appName = "app"; # Available with v1.4 or latest master + enable = true; + projectPath = ./.; + packages = with pkgs; [ + # Native dependencies, e.g. imagemagick + ]; + haskellPackages = p: with p; [ + # Haskell dependencies go here + p.ihp + cabal-install + base + wai + text + + # Uncomment on local development for testing + # hspec + ]; + }; + + # Custom configuration that will start with `devenv up` + devenv.shells.default = { + # Start Mailhog on local development to catch outgoing emails + # services.mailhog.enable = true; + + # Custom processes that don't appear in https://devenv.sh/reference/options/ + processes = { + # Uncomment if you use tailwindcss. + # tailwind.exec = "tailwindcss -c tailwind/tailwind.config.js -i ./tailwind/app.css -o static/app.css --watch=always"; + }; + }; + }; + + # Adding the new NixOS configuration for "qa" + # See https://ihp.digitallyinduced.com/Guide/deployment.html#deploying-with-deploytonixos for more info + # Used to deploy the IHP application to AWS. + # + # Change the `CHANGE-ME` to your correct config. + flake.nixosConfigurations."qa" = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = inputs; + modules = [ + "${nixpkgs}/nixos/modules/virtualisation/amazon-image.nix" + ihp.nixosModules.appWithPostgres + ({ lib, pkgs, ... }: { + + networking.firewall = { + enable = true; + allowedTCPPorts = [ 22 80 443 ]; + }; + + # Enable the Let's encrypt certificate + security.acme.defaults.email = "CHANGE-ME@example.com"; + + # Accept the terms of service of the Let's encrypt provider. + security.acme.acceptTerms = true; + + services.nginx = { + virtualHosts."CHANGE-ME.com" = { + # Uncomment to have http auth with username `foo` and password `bar`. + # basicAuth = { foo = "bar"; }; + }; + }; + + # Logging to AWS CloudWatch + # services.vector = { + # enable = true; + # journaldAccess = true; + # settings = { + # sources.journald = { + # type = "journald"; + # # Log only the services we care about + # include_units = ["app.service" "nginx.service" "worker.service"]; + # }; + + # sinks.out = { + # group_name = "CHANGE-ME"; + # stream_name = "CHANGE-ME"; + # # Change the region to the correct one, e.g. `us-east-1` + # region = "CHANGE-ME"; + # auth = { + # access_key_id = "CHANGE-ME"; + # secret_access_key = "CHANGE-ME"; + # }; + # inputs = ["journald"]; + # type = "aws_cloudwatch_logs"; + # compression = "gzip"; + # encoding.codec = "json"; + # }; + # }; + # }; + + services.ihp = { + domain = "CHANGE-ME.com"; + migrations = ./Application/Migration; + schema = ./Application/Schema.sql; + fixtures = ./Application/Fixtures.sql; + sessionSecret = "CHANGE-ME"; + # Uncomment to use a custom database URL + # databaseUrl = lib.mkForce "postgresql://postgres:...CHANGE-ME"; + + additionalEnvVars = { + # Uncomment to use a custom session secret, ensuring sessions aren't invalidated + # on each deploy. + # Learn how to create the secret key in https://ihp.digitallyinduced.com/Guide/deployment.html#ihpsessionsecret + # IHP_SESSION_SECRET = "CHANGE-ME"; + + SMTP_HOST = "email-smtp.eu-west-1.amazonaws.com"; + SMTP_PORT = "587"; + SMTP_ENCRYPTION = "STARTTLS"; + + SMTP_USER = "CHANGE-ME"; + SMTP_PASSWORD = "CHANGE-ME"; + + AWS_ACCESS_KEY_ID = "CHANGE-ME"; + AWS_SECRET_ACCESS_KEY = "CHANGE-ME"; + }; + }; + # As we use a pre-built AMI on AWS, + # it is essential to enable automatic updates. + # @see https://nixos.wiki/wiki/Automatic_system_upgrades + system.autoUpgrade.enable = true; + # Keep as is. See https://nixos.wiki/wiki/FAQ/When_do_I_update_stateVersion + system.stateVersion = "23.05"; + }) + ]; + }; + + }; +} diff --git a/hie.yaml b/hie.yaml new file mode 100644 index 0000000..7864281 --- /dev/null +++ b/hie.yaml @@ -0,0 +1,4 @@ +# Used by haskell-language-server to find GHC settings +cradle: + bios: + shell: "$IHP/.hie-bios" \ No newline at end of file diff --git a/start b/start new file mode 100755 index 0000000..81464a8 --- /dev/null +++ b/start @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +devenv up diff --git a/static/app.css b/static/app.css new file mode 100644 index 0000000..e69de29 diff --git a/static/app.js b/static/app.js new file mode 100644 index 0000000..1012d94 --- /dev/null +++ b/static/app.js @@ -0,0 +1,3 @@ +$(document).on('ready turbolinks:load', function () { + // This is called on the first page load *and* also when the page is changed by turbolinks +}); \ No newline at end of file diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/static/ihp-welcome-icon.svg b/static/ihp-welcome-icon.svg new file mode 100644 index 0000000..6a3b4dc --- /dev/null +++ b/static/ihp-welcome-icon.svg @@ -0,0 +1 @@ +experience design