mirror of
https://github.com/numtide/treefmt.git
synced 2024-10-05 13:07:17 +03:00
feat: initial import
This commit is contained in:
commit
6904097171
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# editors
|
||||
.idea
|
||||
|
||||
# nix
|
||||
result*
|
||||
repl-result-*
|
||||
|
||||
# direnv
|
||||
/.direnv
|
||||
|
||||
# devshell
|
||||
/.data
|
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Nits Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
187
flake.lock
Normal file
187
flake.lock
Normal file
@ -0,0 +1,187 @@
|
||||
{
|
||||
"nodes": {
|
||||
"devshell": {
|
||||
"inputs": {
|
||||
"nixpkgs": ["nixpkgs"],
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1700815693,
|
||||
"narHash": "sha256-JtKZEQUzosrCwDsLgm+g6aqbP1aseUl1334OShEAS3s=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "7ad1c417c87e98e56dcef7ecd0e0a2f2e5669d51",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1698882062,
|
||||
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-root": {
|
||||
"locked": {
|
||||
"lastModified": 1692742795,
|
||||
"narHash": "sha256-f+Y0YhVCIJ06LemO+3Xx00lIcqQxSKJHXT/yk1RTKxw=",
|
||||
"owner": "srid",
|
||||
"repo": "flake-root",
|
||||
"rev": "d9a70d9c7a5fd7f3258ccf48da9335e9b47c3937",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "srid",
|
||||
"repo": "flake-root",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gomod2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": ["nixpkgs"]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1699950847,
|
||||
"narHash": "sha256-xN/yVtqHb7kimHA/WvQFrEG5WS38t0K+A/W+j/WhQWM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "05c993c9a5bd55a629cd45ed49951557b7e9c61a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1702312524,
|
||||
"narHash": "sha256-gkZJRDBUCpTPBvQk25G0B7vfbpEYM5s5OZqghkjZsnE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a9bf124c46ef298113270b1f84a164865987a91c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1698611440,
|
||||
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "lib",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devshell": "devshell",
|
||||
"flake-parts": "flake-parts",
|
||||
"flake-root": "flake-root",
|
||||
"gomod2nix": "gomod2nix",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": ["nixpkgs"]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1699786194,
|
||||
"narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
39
flake.nix
Normal file
39
flake.nix
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
description = "Treefmt";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
flake-root.url = "github:srid/flake-root";
|
||||
treefmt-nix = {
|
||||
url = "github:numtide/treefmt-nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
devshell = {
|
||||
url = "github:numtide/devshell";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
gomod2nix = {
|
||||
url = "github:nix-community/gomod2nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs @ {flake-parts, ...}:
|
||||
flake-parts.lib.mkFlake
|
||||
{
|
||||
inherit inputs;
|
||||
}
|
||||
{
|
||||
imports = [
|
||||
./nix
|
||||
];
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
};
|
||||
}
|
35
go.mod
Normal file
35
go.mod
Normal file
@ -0,0 +1,35 @@
|
||||
module github.com/numtide/treefmt
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
github.com/adrg/xdg v0.4.0
|
||||
github.com/alecthomas/kong v0.8.1
|
||||
github.com/charmbracelet/log v0.3.1
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/juju/errors v1.0.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1
|
||||
github.com/ztrue/shutdown v0.1.1
|
||||
go.etcd.io/bbolt v1.3.8
|
||||
golang.org/x/sync v0.5.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.9.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
73
go.sum
Normal file
73
go.sum
Normal file
@ -0,0 +1,73 @@
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
|
||||
github.com/alecthomas/assert/v2 v2.1.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA=
|
||||
github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY=
|
||||
github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
|
||||
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||
github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
||||
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
||||
github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw=
|
||||
github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM=
|
||||
github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/ztrue/shutdown v0.1.1 h1:GKR2ye2OSQlq1GNVE/s2NbrIMsFdmL+NdR6z6t1k+Tg=
|
||||
github.com/ztrue/shutdown v0.1.1/go.mod h1:hcMWcM2SwIsQk7Wb49aYme4tX66x6iLzs07w1OYAQLw=
|
||||
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
|
||||
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
81
gomod2nix.toml
Normal file
81
gomod2nix.toml
Normal file
@ -0,0 +1,81 @@
|
||||
schema = 3
|
||||
|
||||
[mod]
|
||||
[mod."github.com/BurntSushi/toml"]
|
||||
version = "v1.3.2"
|
||||
hash = "sha256-FIwyH67KryRWI9Bk4R8s1zFP0IgKR4L66wNQJYQZLeg="
|
||||
[mod."github.com/adrg/xdg"]
|
||||
version = "v0.4.0"
|
||||
hash = "sha256-zGjkdUQmrVqD6rMO9oDY+TeJCpuqnHyvkPCaXDlac/U="
|
||||
[mod."github.com/alecthomas/kong"]
|
||||
version = "v0.8.1"
|
||||
hash = "sha256-170mjSrLNC+0W1KhXltaa+YWYgt5gJQEcfssepcyh4E="
|
||||
[mod."github.com/aymanbagabas/go-osc52/v2"]
|
||||
version = "v2.0.1"
|
||||
hash = "sha256-6Bp0jBZ6npvsYcKZGHHIUSVSTAMEyieweAX2YAKDjjg="
|
||||
[mod."github.com/charmbracelet/lipgloss"]
|
||||
version = "v0.9.1"
|
||||
hash = "sha256-AHbabOymgDRIXsMBgJHS25/GgBWT54oGbd15EBWKeZc="
|
||||
[mod."github.com/charmbracelet/log"]
|
||||
version = "v0.3.1"
|
||||
hash = "sha256-Er60POPID2eNrRZnBHxoI4yHn0mIKnXYftGKSslbXx0="
|
||||
[mod."github.com/davecgh/go-spew"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI="
|
||||
[mod."github.com/go-logfmt/logfmt"]
|
||||
version = "v0.6.0"
|
||||
hash = "sha256-RtIG2qARd5sT10WQ7F3LR8YJhS8exs+KiuUiVf75bWg="
|
||||
[mod."github.com/gobwas/glob"]
|
||||
version = "v0.2.3"
|
||||
hash = "sha256-hYHMUdwxVkMOjSKjR7UWO0D0juHdI4wL8JEy5plu/Jc="
|
||||
[mod."github.com/juju/errors"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-9uZ0wNf44ilzLsvXqOsmFUpNOBFAVadj6+ZH8+QMDMk="
|
||||
[mod."github.com/lucasb-eyer/go-colorful"]
|
||||
version = "v1.2.0"
|
||||
hash = "sha256-Gg9dDJFCTaHrKHRR1SrJgZ8fWieJkybljybkI9x0gyE="
|
||||
[mod."github.com/mattn/go-isatty"]
|
||||
version = "v0.0.18"
|
||||
hash = "sha256-QpIn0DSggtBn2ocyj0RlXDKLK5F5KZG1/ogzrqBCjF8="
|
||||
[mod."github.com/mattn/go-runewidth"]
|
||||
version = "v0.0.15"
|
||||
hash = "sha256-WP39EU2UrQbByYfnwrkBDoKN7xzXsBssDq3pNryBGm0="
|
||||
[mod."github.com/muesli/reflow"]
|
||||
version = "v0.3.0"
|
||||
hash = "sha256-Pou2ybE9SFSZG6YfZLVV1Eyfm+X4FuVpDPLxhpn47Cc="
|
||||
[mod."github.com/muesli/termenv"]
|
||||
version = "v0.15.2"
|
||||
hash = "sha256-Eum/SpyytcNIchANPkG4bYGBgcezLgej7j/+6IhqoMU="
|
||||
[mod."github.com/pmezard/go-difflib"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA="
|
||||
[mod."github.com/rivo/uniseg"]
|
||||
version = "v0.2.0"
|
||||
hash = "sha256-GLj0jiGrT03Ept4V6FXCN1yeZ/b6PpS3MEXK6rYQ8Eg="
|
||||
[mod."github.com/stretchr/testify"]
|
||||
version = "v1.8.4"
|
||||
hash = "sha256-MoOmRzbz9QgiJ+OOBo5h5/LbilhJfRUryvzHJmXAWjo="
|
||||
[mod."github.com/vmihailenco/msgpack/v5"]
|
||||
version = "v5.4.1"
|
||||
hash = "sha256-pDplX6xU6UpNLcFbO1pRREW5vCnSPvSU+ojAwFDv3Hk="
|
||||
[mod."github.com/vmihailenco/tagparser/v2"]
|
||||
version = "v2.0.0"
|
||||
hash = "sha256-M9QyaKhSmmYwsJk7gkjtqu9PuiqZHSmTkous8VWkWY0="
|
||||
[mod."github.com/ztrue/shutdown"]
|
||||
version = "v0.1.1"
|
||||
hash = "sha256-+ygx5THHu9g+vBAn6b63tV35bvQGdRyto4pLhkontJI="
|
||||
[mod."go.etcd.io/bbolt"]
|
||||
version = "v1.3.8"
|
||||
hash = "sha256-ekKy8198B2GfPldHLYZnvNjID6x07dUPYKgFx84TgVs="
|
||||
[mod."golang.org/x/exp"]
|
||||
version = "v0.0.0-20231006140011-7918f672742d"
|
||||
hash = "sha256-2SO1etTQ6UCUhADR5sgvDEDLHcj77pJKCIa/8mGDbAo="
|
||||
[mod."golang.org/x/sync"]
|
||||
version = "v0.5.0"
|
||||
hash = "sha256-EAKeODSsct5HhXPmpWJfulKSCkuUu6kkDttnjyZMNcI="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.13.0"
|
||||
hash = "sha256-/+RDZ0a0oEfJ0k304VqpJpdrl2ZXa3yFlOxy4mjW7w0="
|
||||
[mod."gopkg.in/yaml.v3"]
|
||||
version = "v3.0.1"
|
||||
hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU="
|
144
internal/cache/cache.go
vendored
Normal file
144
internal/cache/cache.go
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/juju/errors"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
const (
|
||||
modifiedBucket = "modified"
|
||||
)
|
||||
|
||||
var db *bolt.DB
|
||||
|
||||
func Open(treeRoot string, clean bool) (err error) {
|
||||
// determine a unique and consistent db name for the tree root
|
||||
h := sha1.New()
|
||||
h.Write([]byte(treeRoot))
|
||||
digest := h.Sum(nil)
|
||||
|
||||
name := base32.StdEncoding.EncodeToString(digest)
|
||||
path, err := xdg.CacheFile(fmt.Sprintf("treefmt/eval-cache/%v.db", name))
|
||||
|
||||
// bust the cache if specified
|
||||
if clean {
|
||||
err := os.Remove(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
err = nil
|
||||
} else if err != nil {
|
||||
return errors.Annotate(err, "failed to clear cache")
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Annotate(err, "could not resolve local path for the cache")
|
||||
}
|
||||
|
||||
db, err = bolt.Open(path, 0o600, nil)
|
||||
if err != nil {
|
||||
return errors.Annotate(err, "failed to open cache")
|
||||
}
|
||||
|
||||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
_, err := tx.CreateBucket([]byte(modifiedBucket))
|
||||
if errors.Is(err, bolt.ErrBucketExists) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Close() error {
|
||||
return db.Close()
|
||||
}
|
||||
|
||||
func ChangeSet(ctx context.Context, root string, pathsCh chan<- string) error {
|
||||
return db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(modifiedBucket))
|
||||
|
||||
return filepath.Walk(root, func(path string, info fs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return errors.Annotate(err, "failed to walk path")
|
||||
} else if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
} else if info.IsDir() {
|
||||
// todo what about symlinks?
|
||||
return nil
|
||||
}
|
||||
|
||||
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
// skip symlinks
|
||||
return nil
|
||||
}
|
||||
|
||||
b := bucket.Get([]byte(path))
|
||||
|
||||
var cached FileInfo
|
||||
|
||||
if b != nil {
|
||||
if err = msgpack.Unmarshal(b, &cached); err != nil {
|
||||
return errors.Annotatef(err, "failed to unmarshal cache info for path '%v'", path)
|
||||
}
|
||||
}
|
||||
|
||||
changedOrNew := !(cached.Modified == info.ModTime() && cached.Size == info.Size())
|
||||
|
||||
if !changedOrNew {
|
||||
// no change
|
||||
return nil
|
||||
}
|
||||
|
||||
// pass on the path
|
||||
pathsCh <- path
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func WriteModTime(paths []string) error {
|
||||
if len(paths) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(modifiedBucket))
|
||||
|
||||
for _, path := range paths {
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
pathInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cacheInfo := FileInfo{
|
||||
Size: pathInfo.Size(),
|
||||
Modified: pathInfo.ModTime(),
|
||||
}
|
||||
|
||||
bytes, err := msgpack.Marshal(cacheInfo)
|
||||
if err != nil {
|
||||
return errors.Annotate(err, "failed to marshal mod time")
|
||||
}
|
||||
|
||||
if err = bucket.Put([]byte(path), bytes); err != nil {
|
||||
return errors.Annotate(err, "failed to put mode time")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
8
internal/cache/types.go
vendored
Normal file
8
internal/cache/types.go
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package cache
|
||||
|
||||
import "time"
|
||||
|
||||
type FileInfo struct {
|
||||
Size int64
|
||||
Modified time.Time
|
||||
}
|
29
internal/cli/cli.go
Normal file
29
internal/cli/cli.go
Normal file
@ -0,0 +1,29 @@
|
||||
package cli
|
||||
|
||||
import "github.com/charmbracelet/log"
|
||||
|
||||
var Cli struct {
|
||||
Log LogOptions `embed:""`
|
||||
|
||||
ConfigFile string `type:"existingfile" default:"./treefmt.toml"`
|
||||
TreeRoot string `type:"existingdir" default:"."`
|
||||
ClearCache bool `short:"c" help:"Reset the evaluation cache. Use in case the cache is not precise enough"`
|
||||
|
||||
Format Format `cmd:"" default:"."`
|
||||
}
|
||||
|
||||
type LogOptions struct {
|
||||
Verbosity int `name:"verbose" short:"v" type:"counter" default:"0" env:"LOG_LEVEL" help:"Set the verbosity of logs e.g. -vv"`
|
||||
}
|
||||
|
||||
func (lo *LogOptions) ConfigureLogger() {
|
||||
log.SetReportTimestamp(false)
|
||||
|
||||
if lo.Verbosity == 0 {
|
||||
log.SetLevel(log.WarnLevel)
|
||||
} else if lo.Verbosity == 1 {
|
||||
log.SetLevel(log.InfoLevel)
|
||||
} else if lo.Verbosity >= 2 {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
}
|
155
internal/cli/format.go
Normal file
155
internal/cli/format.go
Normal file
@ -0,0 +1,155 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/numtide/treefmt/internal/cache"
|
||||
"github.com/numtide/treefmt/internal/format"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/juju/errors"
|
||||
"github.com/ztrue/shutdown"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Format struct{}
|
||||
|
||||
func (f *Format) Run() error {
|
||||
start := time.Now()
|
||||
|
||||
Cli.Log.ConfigureLogger()
|
||||
|
||||
l := log.WithPrefix("format")
|
||||
|
||||
defer func() {
|
||||
if err := cache.Close(); err != nil {
|
||||
l.Errorf("failed to close cache: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// create an overall context
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// register shutdown hook
|
||||
shutdown.Add(cancel)
|
||||
|
||||
// read config
|
||||
cfg, err := format.ReadConfigFile(Cli.ConfigFile)
|
||||
if err != nil {
|
||||
return errors.Annotate(err, "failed to read config file")
|
||||
}
|
||||
|
||||
// init formatters
|
||||
for name, formatter := range cfg.Formatters {
|
||||
if err = formatter.Init(name); err != nil {
|
||||
return errors.Annotatef(err, "failed to initialise formatter: %v", name)
|
||||
}
|
||||
}
|
||||
|
||||
ctx = format.RegisterFormatters(ctx, cfg.Formatters)
|
||||
|
||||
if err = cache.Open(Cli.TreeRoot, Cli.ClearCache); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//
|
||||
pendingCh := make(chan string, 1024)
|
||||
completedCh := make(chan string, 1024)
|
||||
|
||||
ctx = format.SetCompletedChannel(ctx, completedCh)
|
||||
|
||||
//
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
// start the formatters
|
||||
for name := range cfg.Formatters {
|
||||
formatter := cfg.Formatters[name]
|
||||
eg.Go(func() error {
|
||||
return formatter.Run(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
// determine paths to be formatted
|
||||
pathsCh := make(chan string, 1024)
|
||||
|
||||
// update cache as paths are completed
|
||||
eg.Go(func() error {
|
||||
batchSize := 1024
|
||||
batch := make([]string, batchSize)
|
||||
|
||||
var pending, completed int
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-pendingCh:
|
||||
if ok {
|
||||
pending += 1
|
||||
} else if pending == completed {
|
||||
break LOOP
|
||||
}
|
||||
|
||||
case path, ok := <-completedCh:
|
||||
if !ok {
|
||||
break LOOP
|
||||
}
|
||||
batch = append(batch, path)
|
||||
if len(batch) == batchSize {
|
||||
if err := cache.WriteModTime(batch); err != nil {
|
||||
return err
|
||||
}
|
||||
batch = batch[:0]
|
||||
}
|
||||
|
||||
completed += 1
|
||||
|
||||
if completed == pending {
|
||||
close(completedCh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// final flush
|
||||
if err := cache.WriteModTime(batch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
println(fmt.Sprintf("%v files changed in %v", completed, time.Now().Sub(start)))
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
count := 0
|
||||
|
||||
for path := range pathsCh {
|
||||
// todo cycle detection in Befores
|
||||
for _, formatter := range cfg.Formatters {
|
||||
if formatter.Wants(path) {
|
||||
pendingCh <- path
|
||||
count += 1
|
||||
formatter.Put(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, formatter := range cfg.Formatters {
|
||||
formatter.Close()
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
close(completedCh)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
defer close(pathsCh)
|
||||
return cache.ChangeSet(ctx, Cli.TreeRoot, pathsCh)
|
||||
})
|
||||
|
||||
return eg.Wait()
|
||||
}
|
12
internal/format/config.go
Normal file
12
internal/format/config.go
Normal file
@ -0,0 +1,12 @@
|
||||
package format
|
||||
|
||||
import "github.com/BurntSushi/toml"
|
||||
|
||||
type Config struct {
|
||||
Formatters map[string]*Formatter `toml:"formatter"`
|
||||
}
|
||||
|
||||
func ReadConfigFile(path string) (cfg *Config, err error) {
|
||||
_, err = toml.DecodeFile(path, &cfg)
|
||||
return
|
||||
}
|
122
internal/format/config_test.go
Normal file
122
internal/format/config_test.go
Normal file
@ -0,0 +1,122 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
as := require.New(t)
|
||||
|
||||
cfg, err := ReadConfigFile("../../test/treefmt.toml")
|
||||
as.NoError(err, "failed to read config file")
|
||||
|
||||
as.NotNil(cfg)
|
||||
|
||||
// python
|
||||
python, ok := cfg.Formatters["python"]
|
||||
as.True(ok, "python formatter not found")
|
||||
as.Equal("black", python.Command)
|
||||
as.Nil(python.Options)
|
||||
as.Equal([]string{"*.py"}, python.Includes)
|
||||
as.Nil(python.Excludes)
|
||||
|
||||
// elm
|
||||
elm, ok := cfg.Formatters["elm"]
|
||||
as.True(ok, "elm formatter not found")
|
||||
as.Equal("elm-format", elm.Command)
|
||||
as.Equal([]string{"--yes"}, elm.Options)
|
||||
as.Equal([]string{"*.elm"}, elm.Includes)
|
||||
as.Nil(elm.Excludes)
|
||||
|
||||
// go
|
||||
golang, ok := cfg.Formatters["go"]
|
||||
as.True(ok, "go formatter not found")
|
||||
as.Equal("gofmt", golang.Command)
|
||||
as.Equal([]string{"-w"}, golang.Options)
|
||||
as.Equal([]string{"*.go"}, golang.Includes)
|
||||
as.Nil(golang.Excludes)
|
||||
|
||||
// haskell
|
||||
haskell, ok := cfg.Formatters["haskell"]
|
||||
as.True(ok, "haskell formatter not found")
|
||||
as.Equal("ormolu", haskell.Command)
|
||||
as.Equal([]string{
|
||||
"--ghc-opt", "-XBangPatterns",
|
||||
"--ghc-opt", "-XPatternSynonyms",
|
||||
"--ghc-opt", "-XTypeApplications",
|
||||
"--mode", "inplace",
|
||||
"--check-idempotence",
|
||||
}, haskell.Options)
|
||||
as.Equal([]string{"*.hs"}, haskell.Includes)
|
||||
as.Equal([]string{"examples/haskell/"}, haskell.Excludes)
|
||||
|
||||
// nix
|
||||
nix, ok := cfg.Formatters["nix"]
|
||||
as.True(ok, "nix formatter not found")
|
||||
as.Equal("nixpkgs-fmt", nix.Command)
|
||||
as.Nil(nix.Options)
|
||||
as.Equal([]string{"*.nix"}, nix.Includes)
|
||||
as.Equal([]string{"examples/nix/sources.nix"}, nix.Excludes)
|
||||
|
||||
// ruby
|
||||
ruby, ok := cfg.Formatters["ruby"]
|
||||
as.True(ok, "ruby formatter not found")
|
||||
as.Equal("rufo", ruby.Command)
|
||||
as.Equal([]string{"-x"}, ruby.Options)
|
||||
as.Equal([]string{"*.rb"}, ruby.Includes)
|
||||
as.Nil(ruby.Excludes)
|
||||
|
||||
// prettier
|
||||
prettier, ok := cfg.Formatters["prettier"]
|
||||
as.True(ok, "prettier formatter not found")
|
||||
as.Equal("prettier", prettier.Command)
|
||||
as.Equal([]string{"--write"}, prettier.Options)
|
||||
as.Equal([]string{
|
||||
"*.css",
|
||||
"*.html",
|
||||
"*.js",
|
||||
"*.json",
|
||||
"*.jsx",
|
||||
"*.md",
|
||||
"*.mdx",
|
||||
"*.scss",
|
||||
"*.ts",
|
||||
"*.yaml",
|
||||
}, prettier.Includes)
|
||||
as.Equal([]string{"CHANGELOG.md"}, prettier.Excludes)
|
||||
|
||||
// rust
|
||||
// rust, ok := cfg.Formatters["rust"]
|
||||
// as.True(ok, "rust formatter not found")
|
||||
// as.Equal("rustfmt", rust.Command)
|
||||
// as.Equal([]string{"--edition", "2018"}, rust.Options)
|
||||
// as.Equal([]string{"*.rs"}, rust.Includes)
|
||||
// as.Nil(rust.Excludes)
|
||||
|
||||
// shell
|
||||
shell, ok := cfg.Formatters["shell"]
|
||||
as.True(ok, "shell formatter not found")
|
||||
as.Equal("/bin/sh", shell.Command)
|
||||
as.Equal([]string{
|
||||
"-euc",
|
||||
`# First lint all the scripts
|
||||
shellcheck "$@"
|
||||
|
||||
# Then format them
|
||||
shfmt -i 2 -s -w "$@"
|
||||
`,
|
||||
"--",
|
||||
}, shell.Options)
|
||||
as.Equal([]string{"*.sh"}, shell.Includes)
|
||||
as.Nil(shell.Excludes)
|
||||
|
||||
// terraform
|
||||
terraform, ok := cfg.Formatters["terraform"]
|
||||
as.True(ok, "terraform formatter not found")
|
||||
as.Equal("terraform", terraform.Command)
|
||||
as.Equal([]string{"fmt"}, terraform.Options)
|
||||
as.Equal([]string{"*.tf"}, terraform.Includes)
|
||||
as.Nil(terraform.Excludes)
|
||||
}
|
36
internal/format/context.go
Normal file
36
internal/format/context.go
Normal file
@ -0,0 +1,36 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const (
|
||||
formattersKey = "formatters"
|
||||
completedChKey = "completedCh"
|
||||
)
|
||||
|
||||
func RegisterFormatters(ctx context.Context, formatters map[string]*Formatter) context.Context {
|
||||
return context.WithValue(ctx, formattersKey, formatters)
|
||||
}
|
||||
|
||||
func GetFormatters(ctx context.Context) map[string]*Formatter {
|
||||
return ctx.Value(formattersKey).(map[string]*Formatter)
|
||||
}
|
||||
|
||||
func SetCompletedChannel(ctx context.Context, completedCh chan string) context.Context {
|
||||
return context.WithValue(ctx, completedChKey, completedCh)
|
||||
}
|
||||
|
||||
func MarkFormatComplete(ctx context.Context, path string) {
|
||||
ctx.Value(completedChKey).(chan string) <- path
|
||||
}
|
||||
|
||||
func ForwardPath(ctx context.Context, path string, names []string) {
|
||||
if len(names) == 0 {
|
||||
return
|
||||
}
|
||||
formatters := GetFormatters(ctx)
|
||||
for _, name := range names {
|
||||
formatters[name].Put(path)
|
||||
}
|
||||
}
|
161
internal/format/format.go
Normal file
161
internal/format/format.go
Normal file
@ -0,0 +1,161 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
type Formatter struct {
|
||||
Name string
|
||||
Command string
|
||||
Options []string
|
||||
Includes []string
|
||||
Excludes []string
|
||||
Before []string
|
||||
|
||||
log *log.Logger
|
||||
|
||||
// globs for matching against paths
|
||||
includes []glob.Glob
|
||||
excludes []glob.Glob
|
||||
|
||||
inbox chan string
|
||||
|
||||
batch []string
|
||||
batchSize int
|
||||
}
|
||||
|
||||
func (f *Formatter) Init(name string) error {
|
||||
f.Name = name
|
||||
f.log = log.WithPrefix("format | " + name)
|
||||
|
||||
f.inbox = make(chan string, 1024)
|
||||
|
||||
f.batchSize = 1024
|
||||
f.batch = make([]string, f.batchSize)
|
||||
f.batch = f.batch[:0]
|
||||
|
||||
// todo refactor common code below
|
||||
if len(f.Includes) > 0 {
|
||||
for _, pattern := range f.Includes {
|
||||
if !strings.Contains(pattern, "/") {
|
||||
pattern = "**/" + pattern
|
||||
}
|
||||
g, err := glob.Compile(pattern)
|
||||
if err != nil {
|
||||
return errors.Annotatef(err, "failed to compile include pattern '%v' for formatter '%v'", pattern, f.Name)
|
||||
}
|
||||
f.includes = append(f.includes, g)
|
||||
}
|
||||
}
|
||||
|
||||
if len(f.Excludes) > 0 {
|
||||
for _, pattern := range f.Excludes {
|
||||
if !strings.Contains(pattern, "/") {
|
||||
pattern = "**/" + pattern
|
||||
}
|
||||
g, err := glob.Compile(pattern)
|
||||
if err != nil {
|
||||
return errors.Annotatef(err, "failed to compile exclude pattern '%v' for formatter '%v'", pattern, f.Name)
|
||||
}
|
||||
f.excludes = append(f.excludes, g)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Formatter) Wants(path string) bool {
|
||||
if PathMatches(path, f.excludes) {
|
||||
return false
|
||||
}
|
||||
return PathMatches(path, f.includes)
|
||||
}
|
||||
|
||||
func (f *Formatter) Put(path string) {
|
||||
f.inbox <- path
|
||||
}
|
||||
|
||||
func (f *Formatter) Run(ctx context.Context) (err error) {
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
break LOOP
|
||||
|
||||
case path, ok := <-f.inbox:
|
||||
if !ok {
|
||||
break LOOP
|
||||
}
|
||||
|
||||
// add to the current batch
|
||||
f.batch = append(f.batch, path)
|
||||
|
||||
if len(f.batch) == f.batchSize {
|
||||
// drain immediately
|
||||
if err := f.apply(ctx); err != nil {
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// final flush
|
||||
return f.apply(ctx)
|
||||
}
|
||||
|
||||
func (f *Formatter) apply(ctx context.Context) error {
|
||||
// empty check
|
||||
if len(f.batch) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// construct args, starting with config
|
||||
args := f.Options
|
||||
|
||||
// append each file path
|
||||
for _, path := range f.batch {
|
||||
args = append(args, path)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
cmd := exec.CommandContext(ctx, f.Command, args...)
|
||||
|
||||
if _, err := cmd.CombinedOutput(); err != nil {
|
||||
// todo log output
|
||||
return err
|
||||
}
|
||||
|
||||
f.log.Infof("%v files processed in %v", len(f.batch), time.Now().Sub(start))
|
||||
|
||||
// mark completed or forward on
|
||||
if len(f.Before) == 0 {
|
||||
for _, path := range f.batch {
|
||||
MarkFormatComplete(ctx, path)
|
||||
}
|
||||
} else {
|
||||
for _, path := range f.batch {
|
||||
ForwardPath(ctx, path, f.Before)
|
||||
}
|
||||
}
|
||||
|
||||
// reset batch
|
||||
f.batch = f.batch[:0]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Formatter) Close() {
|
||||
close(f.inbox)
|
||||
}
|
15
internal/format/glob.go
Normal file
15
internal/format/glob.go
Normal file
@ -0,0 +1,15 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"github.com/gobwas/glob"
|
||||
)
|
||||
|
||||
func PathMatches(path string, globs []glob.Glob) bool {
|
||||
for idx := range globs {
|
||||
if globs[idx].Match(path) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
21
internal/log/writer.go
Normal file
21
internal/log/writer.go
Normal file
@ -0,0 +1,21 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
Log *log.Logger
|
||||
}
|
||||
|
||||
func (l *Writer) Write(p []byte) (n int, err error) {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(p))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
l.Log.Debug(line)
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
11
main.go
Normal file
11
main.go
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/numtide/treefmt/internal/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := kong.Parse(&cli.Cli)
|
||||
ctx.FatalIfErrorf(ctx.Run())
|
||||
}
|
5
nix/checks.nix
Normal file
5
nix/checks.nix
Normal file
@ -0,0 +1,5 @@
|
||||
{lib, ...}: {
|
||||
perSystem = {self', ...}: {
|
||||
checks = with lib; mapAttrs' (n: nameValuePair "package-${n}") self'.packages;
|
||||
};
|
||||
}
|
10
nix/default.nix
Normal file
10
nix/default.nix
Normal file
@ -0,0 +1,10 @@
|
||||
{inputs, ...}: {
|
||||
imports = [
|
||||
inputs.flake-root.flakeModule
|
||||
./checks.nix
|
||||
./devshell.nix
|
||||
./nixpkgs.nix
|
||||
./packages.nix
|
||||
./treefmt.nix
|
||||
];
|
||||
}
|
58
nix/devshell.nix
Normal file
58
nix/devshell.nix
Normal file
@ -0,0 +1,58 @@
|
||||
{inputs, ...}: {
|
||||
imports = [
|
||||
inputs.devshell.flakeModule
|
||||
];
|
||||
|
||||
config.perSystem = {
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
config.devshells.default = {
|
||||
env = [
|
||||
{
|
||||
name = "GOROOT";
|
||||
value = pkgs.go + "/share/go";
|
||||
}
|
||||
{
|
||||
name = "LD_LIBRARY_PATH";
|
||||
value = "$DEVSHELL_DIR/lib";
|
||||
}
|
||||
];
|
||||
|
||||
packages = with pkgs; [
|
||||
# golang
|
||||
go
|
||||
go-tools
|
||||
delve
|
||||
golangci-lint
|
||||
|
||||
# formatters for testing
|
||||
|
||||
elmPackages.elm-format
|
||||
haskellPackages.cabal-fmt
|
||||
haskellPackages.ormolu
|
||||
mdsh
|
||||
nixpkgs-fmt
|
||||
nodePackages.prettier
|
||||
python3.pkgs.black
|
||||
rufo
|
||||
rustfmt
|
||||
shellcheck
|
||||
shfmt
|
||||
terraform
|
||||
];
|
||||
|
||||
commands = [
|
||||
{
|
||||
category = "development";
|
||||
package = pkgs.gomod2nix;
|
||||
}
|
||||
{
|
||||
category = "development";
|
||||
package = pkgs.enumer;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
16
nix/nixpkgs.nix
Normal file
16
nix/nixpkgs.nix
Normal file
@ -0,0 +1,16 @@
|
||||
{inputs, ...}: {
|
||||
perSystem = {system, ...}: {
|
||||
# customise nixpkgs instance
|
||||
_module.args.pkgs = import inputs.nixpkgs {
|
||||
inherit system;
|
||||
overlays = [
|
||||
inputs.gomod2nix.overlays.default
|
||||
];
|
||||
config = {
|
||||
# for terraform
|
||||
# todo make this more specific
|
||||
allowUnfree = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
42
nix/packages.nix
Normal file
42
nix/packages.nix
Normal file
@ -0,0 +1,42 @@
|
||||
{inputs, ...}: {
|
||||
imports = [
|
||||
inputs.flake-parts.flakeModules.easyOverlay
|
||||
];
|
||||
|
||||
perSystem = {
|
||||
self',
|
||||
inputs',
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
packages = rec {
|
||||
treefmt = inputs'.gomod2nix.legacyPackages.buildGoApplication rec {
|
||||
pname = "treefmt";
|
||||
version = "0.0.1+dev";
|
||||
|
||||
# ensure we are using the same version of go to build with
|
||||
inherit (pkgs) go;
|
||||
|
||||
src = ../.;
|
||||
modules = ../gomod2nix.toml;
|
||||
|
||||
ldflags = [
|
||||
"-X 'build.Name=${pname}'"
|
||||
"-X 'build.Version=${version}'"
|
||||
];
|
||||
|
||||
meta = with lib; {
|
||||
description = "treefmt: one CLI to format your repo";
|
||||
homepage = "https://github.com/numtide/treefmt";
|
||||
license = licenses.mit;
|
||||
mainProgram = "treefmt";
|
||||
};
|
||||
};
|
||||
|
||||
default = treefmt;
|
||||
};
|
||||
|
||||
overlayAttrs = self'.packages;
|
||||
};
|
||||
}
|
32
nix/treefmt.nix
Normal file
32
nix/treefmt.nix
Normal file
@ -0,0 +1,32 @@
|
||||
{inputs, ...}: {
|
||||
imports = [
|
||||
inputs.treefmt-nix.flakeModule
|
||||
];
|
||||
perSystem = {config, ...}: {
|
||||
treefmt.config = {
|
||||
inherit (config.flake-root) projectRootFile;
|
||||
flakeCheck = true;
|
||||
flakeFormatter = true;
|
||||
programs = {
|
||||
alejandra.enable = true;
|
||||
deadnix.enable = true;
|
||||
gofumpt.enable = true;
|
||||
prettier.enable = true;
|
||||
statix.enable = true;
|
||||
};
|
||||
|
||||
settings.formatter.prettier.options = ["--tab-width" "4"];
|
||||
};
|
||||
|
||||
devshells.default = {
|
||||
commands = [
|
||||
{
|
||||
category = "formatting";
|
||||
name = "fmt";
|
||||
help = "format the repo";
|
||||
command = "nix fmt";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
3
test/echo.toml
Normal file
3
test/echo.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[formatter.echo]
|
||||
command = "echo"
|
||||
includes = [ "*.*" ]
|
22
test/examples/elm/elm.json
Normal file
22
test/examples/elm/elm.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"type": "application",
|
||||
"source-directories": ["src"],
|
||||
"elm-version": "0.19.1",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"elm/browser": "1.0.2",
|
||||
"elm/core": "1.0.5",
|
||||
"elm/html": "1.0.0"
|
||||
},
|
||||
"indirect": {
|
||||
"elm/json": "1.1.3",
|
||||
"elm/time": "1.0.0",
|
||||
"elm/url": "1.0.0",
|
||||
"elm/virtual-dom": "1.0.2"
|
||||
}
|
||||
},
|
||||
"test-dependencies": {
|
||||
"direct": {},
|
||||
"indirect": {}
|
||||
}
|
||||
}
|
31
test/examples/elm/src/Main.elm
Normal file
31
test/examples/elm/src/Main.elm
Normal file
@ -0,0 +1,31 @@
|
||||
module Main exposing (Msg(..), main, update, view)
|
||||
|
||||
import Browser
|
||||
import Html exposing (Html, button, div, text)
|
||||
import Html.Events exposing (onClick)
|
||||
|
||||
|
||||
main =
|
||||
Browser.sandbox { init = 0, update = update, view = view }
|
||||
|
||||
|
||||
type Msg
|
||||
= Increment
|
||||
| Decrement
|
||||
|
||||
|
||||
update msg model =
|
||||
case msg of
|
||||
Increment ->
|
||||
model + 1
|
||||
|
||||
Decrement ->
|
||||
model - 1
|
||||
|
||||
|
||||
view model =
|
||||
div []
|
||||
[ button [ onClick Decrement ] [ text "-" ]
|
||||
, div [] [ text (String.fromInt model) ]
|
||||
, button [ onClick Increment ] [ text "+" ]
|
||||
]
|
3
test/examples/go/go.mod
Normal file
3
test/examples/go/go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module hello
|
||||
|
||||
go 1.15
|
7
test/examples/go/main.go
Normal file
7
test/examples/go/main.go
Normal file
@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello world")
|
||||
}
|
5
test/examples/haskell-frontend/CHANGELOG.md
Normal file
5
test/examples/haskell-frontend/CHANGELOG.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Revision history for haskell
|
||||
|
||||
## 0.1.0.0 -- YYYY-mm-dd
|
||||
|
||||
- First version. Released on an unsuspecting world.
|
4
test/examples/haskell-frontend/Main.hs
Normal file
4
test/examples/haskell-frontend/Main.hs
Normal file
@ -0,0 +1,4 @@
|
||||
module Main where
|
||||
|
||||
main :: IO ()
|
||||
main = putStrLn "Hello, Haskell!"
|
3
test/examples/haskell-frontend/Setup.hs
Normal file
3
test/examples/haskell-frontend/Setup.hs
Normal file
@ -0,0 +1,3 @@
|
||||
import Distribution.Simple
|
||||
|
||||
main = defaultMain
|
25
test/examples/haskell-frontend/haskell-frontend.cabal
Normal file
25
test/examples/haskell-frontend/haskell-frontend.cabal
Normal file
@ -0,0 +1,25 @@
|
||||
cabal-version: >=1.10
|
||||
-- Initial package description 'haskell.cabal' generated by 'cabal init'.
|
||||
-- For further documentation, see http://haskell.org/cabal/users-guide/
|
||||
|
||||
name: haskell-frontend
|
||||
version: 0.1.0.0
|
||||
-- synopsis:
|
||||
-- description:
|
||||
-- bug-reports:
|
||||
-- license:
|
||||
license-file: LICENSE
|
||||
author: Andika Demas Riyandi
|
||||
maintainer: andika.riyan@gmail.com
|
||||
-- copyright:
|
||||
-- category:
|
||||
build-type: Simple
|
||||
extra-source-files: CHANGELOG.md
|
||||
|
||||
executable haskell-frontend
|
||||
main-is: Main.hs
|
||||
-- other-modules:
|
||||
-- other-extensions:
|
||||
build-depends: base >=4.14 && <4.15
|
||||
-- hs-source-dirs:
|
||||
default-language: Haskell2010
|
5
test/examples/haskell/CHANGELOG.md
Normal file
5
test/examples/haskell/CHANGELOG.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Revision history for haskell
|
||||
|
||||
## 0.1.0.0 -- YYYY-mm-dd
|
||||
|
||||
- First version. Released on an unsuspecting world.
|
4
test/examples/haskell/Foo.hs
Normal file
4
test/examples/haskell/Foo.hs
Normal file
@ -0,0 +1,4 @@
|
||||
module Foo where
|
||||
|
||||
foo :: IO ()
|
||||
foo = putStrLn "Hello, Riyan!"
|
4
test/examples/haskell/Main.hs
Normal file
4
test/examples/haskell/Main.hs
Normal file
@ -0,0 +1,4 @@
|
||||
module Main where
|
||||
|
||||
main :: IO ()
|
||||
main = putStrLn "Hello, Riyan!"
|
4
test/examples/haskell/Nested/Foo.hs
Normal file
4
test/examples/haskell/Nested/Foo.hs
Normal file
@ -0,0 +1,4 @@
|
||||
module Nested.Foo where
|
||||
|
||||
foo :: IO ()
|
||||
foo = putStrLn "Hello, Riyan!"
|
3
test/examples/haskell/Setup.hs
Normal file
3
test/examples/haskell/Setup.hs
Normal file
@ -0,0 +1,3 @@
|
||||
import Distribution.Simple
|
||||
|
||||
main = defaultMain
|
25
test/examples/haskell/haskell.cabal
Normal file
25
test/examples/haskell/haskell.cabal
Normal file
@ -0,0 +1,25 @@
|
||||
cabal-version: >=1.10
|
||||
-- Initial package description 'haskell.cabal' generated by 'cabal init'.
|
||||
-- For further documentation, see http://haskell.org/cabal/users-guide/
|
||||
|
||||
name: haskell
|
||||
version: 0.1.0.0
|
||||
-- synopsis:
|
||||
-- description:
|
||||
-- bug-reports:
|
||||
-- license:
|
||||
license-file: LICENSE
|
||||
author: Andika Demas Riyandi
|
||||
maintainer: andika.riyan@gmail.com
|
||||
-- copyright:
|
||||
-- category:
|
||||
build-type: Simple
|
||||
extra-source-files: CHANGELOG.md
|
||||
|
||||
executable haskell
|
||||
main-is: Main.hs
|
||||
-- other-modules:
|
||||
-- other-extensions:
|
||||
build-depends: base >=4.14 && <4.15
|
||||
-- hs-source-dirs:
|
||||
default-language: Haskell2010
|
10
test/examples/haskell/treefmt.toml
Normal file
10
test/examples/haskell/treefmt.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[formatter.haskell]
|
||||
command = "ormolu"
|
||||
options = [
|
||||
"--ghc-opt", "-XBangPatterns",
|
||||
"--ghc-opt", "-XPatternSynonyms",
|
||||
"--ghc-opt", "-XTypeApplications",
|
||||
"--mode", "inplace",
|
||||
"--check-idempotence",
|
||||
]
|
||||
includes = ["Foo.hs"]
|
10
test/examples/html/index.html
Normal file
10
test/examples/html/index.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
</body>
|
||||
</html>
|
0
test/examples/html/scripts/.gitkeep
Normal file
0
test/examples/html/scripts/.gitkeep
Normal file
65
test/examples/javascript/source/hello.js
Normal file
65
test/examples/javascript/source/hello.js
Normal file
@ -0,0 +1,65 @@
|
||||
const helloFactory = function ({ React }) {
|
||||
const { string, func } = React.PropTypes;
|
||||
|
||||
return function Hello(props) {
|
||||
// React wants propTypes & defaultProps
|
||||
// to be static.
|
||||
Hello.propTypes = {
|
||||
word: string,
|
||||
mode: string,
|
||||
|
||||
actions: React.PropTypes.shape({
|
||||
setWord: func.isRequired,
|
||||
setMode: func.isRequired,
|
||||
}),
|
||||
};
|
||||
|
||||
return {
|
||||
props, // set props
|
||||
|
||||
componentDidUpdate() {
|
||||
this.refs.wordInput.getDOMNode().focus();
|
||||
},
|
||||
|
||||
render() {
|
||||
const { word, mode } = this.props;
|
||||
|
||||
const { setMode, setWord } = this.props.actions;
|
||||
|
||||
const styles = {
|
||||
displayMode: {
|
||||
display: mode === "display" ? "inline" : "none",
|
||||
},
|
||||
|
||||
editMode: {
|
||||
display: mode === "edit" ? "inline" : "none",
|
||||
},
|
||||
};
|
||||
|
||||
const onKeyUp = function (e) {
|
||||
if (e.key !== "Enter") return;
|
||||
|
||||
setWord(e.target.value);
|
||||
setMode("display");
|
||||
};
|
||||
|
||||
return (
|
||||
<p>
|
||||
Hello,
|
||||
<span style={styles.displayMode} onClick={() => setMode("edit")}>
|
||||
{word}!
|
||||
</span>
|
||||
<input
|
||||
ref="wordInput"
|
||||
style={styles.editMode}
|
||||
placeholder={word}
|
||||
onKeyUp={onKeyUp}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export default helloFactory;
|
242
test/examples/nix/sources.nix
Normal file
242
test/examples/nix/sources.nix
Normal file
@ -0,0 +1,242 @@
|
||||
# This file has been generated by Niv.
|
||||
let
|
||||
#
|
||||
# The fetchers. fetch_<type> fetches specs of type <type>.
|
||||
#
|
||||
fetch_file = pkgs: name: spec:
|
||||
let
|
||||
name' = sanitizeName name + "-src";
|
||||
in
|
||||
if spec.builtin or true
|
||||
then
|
||||
builtins_fetchurl
|
||||
{
|
||||
inherit (spec) url sha256;
|
||||
name = name';
|
||||
}
|
||||
else
|
||||
pkgs.fetchurl {
|
||||
inherit (spec) url sha256;
|
||||
name = name';
|
||||
};
|
||||
|
||||
fetch_tarball = pkgs: name: spec:
|
||||
let
|
||||
name' = sanitizeName name + "-src";
|
||||
in
|
||||
if spec.builtin or true
|
||||
then
|
||||
builtins_fetchTarball
|
||||
{
|
||||
name = name';
|
||||
inherit (spec) url sha256;
|
||||
}
|
||||
else
|
||||
pkgs.fetchzip {
|
||||
name = name';
|
||||
inherit (spec) url sha256;
|
||||
};
|
||||
|
||||
fetch_git = name: spec:
|
||||
let
|
||||
ref =
|
||||
spec.ref
|
||||
or (
|
||||
if spec ? branch
|
||||
then "refs/heads/${spec.branch}"
|
||||
else if spec ? tag
|
||||
then "refs/tags/${spec.tag}"
|
||||
else abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"
|
||||
);
|
||||
in
|
||||
builtins.fetchGit {
|
||||
url = spec.repo;
|
||||
inherit (spec) rev;
|
||||
inherit ref;
|
||||
};
|
||||
|
||||
fetch_local = spec: spec.path;
|
||||
|
||||
fetch_builtin-tarball = name:
|
||||
throw
|
||||
'' [${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
|
||||
$ niv modify ${name} -a type=tarball -a builtin=true'';
|
||||
|
||||
fetch_builtin-url = name:
|
||||
throw
|
||||
'' [${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
|
||||
$ niv modify ${name} -a type=file -a builtin=true'';
|
||||
|
||||
#
|
||||
# Various helpers
|
||||
#
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
|
||||
sanitizeName = name: (
|
||||
concatMapStrings
|
||||
(s:
|
||||
if builtins.isList s
|
||||
then "-"
|
||||
else s)
|
||||
(
|
||||
builtins.split "[^[:alnum:]+._?=-]+"
|
||||
((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
|
||||
)
|
||||
);
|
||||
|
||||
# The set of packages used when specs are fetched using non-builtins.
|
||||
mkPkgs = sources: system:
|
||||
let
|
||||
sourcesNixpkgs =
|
||||
import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
|
||||
hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
|
||||
hasThisAsNixpkgsPath = <nixpkgs> == ./.;
|
||||
in
|
||||
if builtins.hasAttr "nixpkgs" sources
|
||||
then sourcesNixpkgs
|
||||
else if hasNixpkgsPath && ! hasThisAsNixpkgsPath
|
||||
then import <nixpkgs> { }
|
||||
else
|
||||
abort
|
||||
''
|
||||
Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
|
||||
add a package called "nixpkgs" to your sources.json.
|
||||
'';
|
||||
|
||||
# The actual fetching function.
|
||||
fetch = pkgs: name: spec:
|
||||
if ! builtins.hasAttr "type" spec
|
||||
then abort "ERROR: niv spec ${name} does not have a 'type' attribute"
|
||||
else if spec.type == "file"
|
||||
then fetch_file pkgs name spec
|
||||
else if spec.type == "tarball"
|
||||
then fetch_tarball pkgs name spec
|
||||
else if spec.type == "git"
|
||||
then fetch_git name spec
|
||||
else if spec.type == "local"
|
||||
then fetch_local spec
|
||||
else if spec.type == "builtin-tarball"
|
||||
then fetch_builtin-tarball name
|
||||
else if spec.type == "builtin-url"
|
||||
then fetch_builtin-url name
|
||||
else abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
|
||||
|
||||
# If the environment variable NIV_OVERRIDE_${name} is set, then use
|
||||
# the path directly as opposed to the fetched source.
|
||||
replace = name: drv:
|
||||
let
|
||||
saneName =
|
||||
stringAsChars
|
||||
(c:
|
||||
if ((builtins.match "[a-zA-Z0-9]" c) == null)
|
||||
then "_"
|
||||
else c)
|
||||
name;
|
||||
ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
|
||||
in
|
||||
if ersatz == ""
|
||||
then drv
|
||||
else
|
||||
# this turns the string into an actual Nix path (for both absolute and
|
||||
# relative paths)
|
||||
if builtins.substring 0 1 ersatz == "/"
|
||||
then /. + ersatz
|
||||
else /. + builtins.getEnv "PWD" + "/${ersatz}";
|
||||
|
||||
# Ports of functions for older nix versions
|
||||
|
||||
# a Nix version of mapAttrs if the built-in doesn't exist
|
||||
mapAttrs =
|
||||
builtins.mapAttrs
|
||||
or (
|
||||
f: set:
|
||||
with builtins;
|
||||
listToAttrs (map
|
||||
(attr: {
|
||||
name = attr;
|
||||
value = f attr set.${attr};
|
||||
})
|
||||
(attrNames set))
|
||||
);
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
|
||||
range = first: last:
|
||||
if first > last
|
||||
then [ ]
|
||||
else builtins.genList (n: first + n) (last - first + 1);
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
|
||||
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
|
||||
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
|
||||
concatMapStrings = f: list: concatStrings (map f list);
|
||||
concatStrings = builtins.concatStringsSep "";
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
|
||||
optionalAttrs = cond: as:
|
||||
if cond
|
||||
then as
|
||||
else { };
|
||||
|
||||
# fetchTarball version that is compatible between all the versions of Nix
|
||||
builtins_fetchTarball =
|
||||
{ url
|
||||
, name ? null
|
||||
, sha256
|
||||
,
|
||||
} @ attrs:
|
||||
let
|
||||
inherit (builtins) lessThan nixVersion fetchTarball;
|
||||
in
|
||||
if lessThan nixVersion "1.12"
|
||||
then fetchTarball ({ inherit url; } // (optionalAttrs (name != null) { inherit name; }))
|
||||
else fetchTarball attrs;
|
||||
|
||||
# fetchurl version that is compatible between all the versions of Nix
|
||||
builtins_fetchurl =
|
||||
{ url
|
||||
, name ? null
|
||||
, sha256
|
||||
,
|
||||
} @ attrs:
|
||||
let
|
||||
inherit (builtins) lessThan nixVersion fetchurl;
|
||||
in
|
||||
if lessThan nixVersion "1.12"
|
||||
then fetchurl ({ inherit url; } // (optionalAttrs (name != null) { inherit name; }))
|
||||
else fetchurl attrs;
|
||||
|
||||
# Create the final "sources" from the config
|
||||
mkSources = config:
|
||||
mapAttrs
|
||||
(
|
||||
name: spec:
|
||||
if builtins.hasAttr "outPath" spec
|
||||
then
|
||||
abort
|
||||
"The values in sources.json should not have an 'outPath' attribute"
|
||||
else spec // { outPath = replace name (fetch config.pkgs name spec); }
|
||||
)
|
||||
config.sources;
|
||||
|
||||
# The "config" used by the fetchers
|
||||
mkConfig =
|
||||
{ sourcesFile ? if builtins.pathExists ./sources.json
|
||||
then ./sources.json
|
||||
else null
|
||||
, sources ? if (sourcesFile == null)
|
||||
then { }
|
||||
else builtins.fromJSON (builtins.readFile sourcesFile)
|
||||
, system ? builtins.currentSystem
|
||||
, pkgs ? mkPkgs sources system
|
||||
,
|
||||
}: rec {
|
||||
# The sources, i.e. the attribute set of spec name to spec
|
||||
inherit sources;
|
||||
|
||||
# The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
|
||||
inherit pkgs;
|
||||
};
|
||||
in
|
||||
mkSources (mkConfig { }) // { __functor = _: settings: mkSources (mkConfig settings); }
|
12
test/examples/python/main.py
Normal file
12
test/examples/python/main.py
Normal file
@ -0,0 +1,12 @@
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def hello_world():
|
||||
return "Hello world"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
1
test/examples/python/requirements.txt
Normal file
1
test/examples/python/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
Flask==0.12.1
|
104
test/examples/python/virtualenv_proxy.py
Normal file
104
test/examples/python/virtualenv_proxy.py
Normal file
@ -0,0 +1,104 @@
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
|
||||
def to_str(value):
|
||||
return value.decode(sys.getfilesystemencoding())
|
||||
|
||||
def execfile(path, global_dict):
|
||||
"""Execute a file"""
|
||||
with open(path, "r") as f:
|
||||
code = f.read()
|
||||
code = code.replace("\r\n", "\n") + "\n"
|
||||
exec(code, global_dict)
|
||||
|
||||
else:
|
||||
|
||||
def to_str(value):
|
||||
return value.encode(sys.getfilesystemencoding())
|
||||
|
||||
|
||||
def log(txt):
|
||||
"""Logs fatal errors to a log file if WSGI_LOG env var is defined"""
|
||||
log_file = os.environ.get("WSGI_LOG")
|
||||
if log_file:
|
||||
f = open(log_file, "a+")
|
||||
try:
|
||||
f.write("%s: %s" % (datetime.datetime.now(), txt))
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
|
||||
def get_wsgi_handler(handler_name):
|
||||
if not handler_name:
|
||||
raise Exception("WSGI_ALT_VIRTUALENV_HANDLER env var must be set")
|
||||
|
||||
if not isinstance(handler_name, str):
|
||||
handler_name = to_str(handler_name)
|
||||
|
||||
module_name, _, callable_name = handler_name.rpartition(".")
|
||||
should_call = callable_name.endswith("()")
|
||||
callable_name = callable_name[:-2] if should_call else callable_name
|
||||
name_list = [(callable_name, should_call)]
|
||||
handler = None
|
||||
last_tb = ""
|
||||
|
||||
while module_name:
|
||||
try:
|
||||
handler = __import__(module_name, fromlist=[name_list[0][0]])
|
||||
last_tb = ""
|
||||
for name, should_call in name_list:
|
||||
handler = getattr(handler, name)
|
||||
if should_call:
|
||||
handler = handler()
|
||||
break
|
||||
except ImportError:
|
||||
module_name, _, callable_name = module_name.rpartition(".")
|
||||
should_call = callable_name.endswith("()")
|
||||
callable_name = callable_name[:-2] if should_call else callable_name
|
||||
name_list.insert(0, (callable_name, should_call))
|
||||
handler = None
|
||||
last_tb = ": " + traceback.format_exc()
|
||||
|
||||
if handler is None:
|
||||
raise ValueError('"%s" could not be imported%s' % (handler_name, last_tb))
|
||||
|
||||
return handler
|
||||
|
||||
|
||||
activate_this = os.getenv("WSGI_ALT_VIRTUALENV_ACTIVATE_THIS")
|
||||
if not activate_this:
|
||||
raise Exception("WSGI_ALT_VIRTUALENV_ACTIVATE_THIS is not set")
|
||||
|
||||
|
||||
def get_virtualenv_handler():
|
||||
log("Activating virtualenv with %s\n" % activate_this)
|
||||
execfile(activate_this, dict(__file__=activate_this))
|
||||
|
||||
log("Getting handler %s\n" % os.getenv("WSGI_ALT_VIRTUALENV_HANDLER"))
|
||||
handler = get_wsgi_handler(os.getenv("WSGI_ALT_VIRTUALENV_HANDLER"))
|
||||
log("Got handler: %r\n" % handler)
|
||||
return handler
|
||||
|
||||
|
||||
def get_venv_handler():
|
||||
log("Activating venv with executable at %s\n" % activate_this)
|
||||
import site
|
||||
|
||||
sys.executable = activate_this
|
||||
old_sys_path, sys.path = sys.path, []
|
||||
|
||||
site.main()
|
||||
|
||||
sys.path.insert(0, "")
|
||||
for item in old_sys_path:
|
||||
if item not in sys.path:
|
||||
sys.path.append(item)
|
||||
|
||||
log("Getting handler %s\n" % os.getenv("WSGI_ALT_VIRTUALENV_HANDLER"))
|
||||
handler = get_wsgi_handler(os.getenv("WSGI_ALT_VIRTUALENV_HANDLER"))
|
||||
log("Got handler: %r\n" % handler)
|
||||
return handler
|
452
test/examples/ruby/bundler.rb
Normal file
452
test/examples/ruby/bundler.rb
Normal file
@ -0,0 +1,452 @@
|
||||
# frozen_string_literal: true
|
||||
require "fileutils"
|
||||
require "pathname"
|
||||
require "rbconfig"
|
||||
require "thread"
|
||||
require "bundler/environment_preserver"
|
||||
require "bundler/gem_remote_fetcher"
|
||||
require "bundler/rubygems_ext"
|
||||
require "bundler/rubygems_integration"
|
||||
require "bundler/version"
|
||||
require "bundler/constants"
|
||||
require "bundler/current_ruby"
|
||||
require "bundler/errors"
|
||||
|
||||
module Bundler
|
||||
environment_preserver = EnvironmentPreserver.new(ENV, %w(PATH GEM_PATH))
|
||||
ORIGINAL_ENV = environment_preserver.restore
|
||||
ENV.replace(environment_preserver.backup)
|
||||
SUDO_MUTEX = Mutex.new
|
||||
|
||||
autoload :Definition, "bundler/definition"
|
||||
autoload :Dependency, "bundler/dependency"
|
||||
autoload :DepProxy, "bundler/dep_proxy"
|
||||
autoload :Deprecate, "bundler/deprecate"
|
||||
autoload :Dsl, "bundler/dsl"
|
||||
autoload :EndpointSpecification, "bundler/endpoint_specification"
|
||||
autoload :Environment, "bundler/environment"
|
||||
autoload :Env, "bundler/env"
|
||||
autoload :Fetcher, "bundler/fetcher"
|
||||
autoload :GemHelper, "bundler/gem_helper"
|
||||
autoload :GemHelpers, "bundler/gem_helpers"
|
||||
autoload :Graph, "bundler/graph"
|
||||
autoload :Index, "bundler/index"
|
||||
autoload :Installer, "bundler/installer"
|
||||
autoload :Injector, "bundler/injector"
|
||||
autoload :LazySpecification, "bundler/lazy_specification"
|
||||
autoload :LockfileParser, "bundler/lockfile_parser"
|
||||
autoload :MatchPlatform, "bundler/match_platform"
|
||||
autoload :Mirror, "bundler/mirror"
|
||||
autoload :Mirrors, "bundler/mirror"
|
||||
autoload :RemoteSpecification, "bundler/remote_specification"
|
||||
autoload :Resolver, "bundler/resolver"
|
||||
autoload :Retry, "bundler/retry"
|
||||
autoload :RubyVersion, "bundler/ruby_version"
|
||||
autoload :RubyDsl, "bundler/ruby_dsl"
|
||||
autoload :Runtime, "bundler/runtime"
|
||||
autoload :Settings, "bundler/settings"
|
||||
autoload :SharedHelpers, "bundler/shared_helpers"
|
||||
autoload :SpecSet, "bundler/spec_set"
|
||||
autoload :StubSpecification, "bundler/stub_specification"
|
||||
autoload :Source, "bundler/source"
|
||||
autoload :SourceList, "bundler/source_list"
|
||||
autoload :RubyGemsGemInstaller, "bundler/rubygems_gem_installer"
|
||||
autoload :UI, "bundler/ui"
|
||||
|
||||
class << self
|
||||
attr_writer :bundle_path
|
||||
|
||||
def configure
|
||||
@configured ||= configure_gem_home_and_path
|
||||
end
|
||||
|
||||
def ui
|
||||
(defined?(@ui) && @ui) || (self.ui = UI::Silent.new)
|
||||
end
|
||||
|
||||
def ui=(ui)
|
||||
Bundler.rubygems.ui = ui ? UI::RGProxy.new(ui) : nil
|
||||
@ui = ui
|
||||
end
|
||||
|
||||
# Returns absolute path of where gems are installed on the filesystem.
|
||||
def bundle_path
|
||||
@bundle_path ||= Pathname.new(settings.path).expand_path(root)
|
||||
end
|
||||
|
||||
# Returns absolute location of where binstubs are installed to.
|
||||
def bin_path
|
||||
@bin_path ||= begin
|
||||
path = settings[:bin] || "bin"
|
||||
path = Pathname.new(path).expand_path(root).expand_path
|
||||
SharedHelpers.filesystem_access(path) { |p| FileUtils.mkdir_p(p) }
|
||||
path
|
||||
end
|
||||
end
|
||||
|
||||
def setup(*groups)
|
||||
# Return if all groups are already loaded
|
||||
return @setup if defined?(@setup)
|
||||
|
||||
definition.validate_ruby!
|
||||
|
||||
if groups.empty?
|
||||
# Load all groups, but only once
|
||||
@setup = load.setup
|
||||
else
|
||||
load.setup(*groups)
|
||||
end
|
||||
end
|
||||
|
||||
def require(*groups)
|
||||
setup(*groups).require(*groups)
|
||||
end
|
||||
|
||||
def load
|
||||
@load ||= Runtime.new(root, definition)
|
||||
end
|
||||
|
||||
def environment
|
||||
Bundler::Environment.new(root, definition)
|
||||
end
|
||||
|
||||
# Returns an instance of Bundler::Definition for given Gemfile and lockfile
|
||||
#
|
||||
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
||||
# to be updated or true if all gems should be updated
|
||||
# @return [Bundler::Definition]
|
||||
def definition(unlock = nil)
|
||||
@definition = nil if unlock
|
||||
@definition ||= begin
|
||||
configure
|
||||
upgrade_lockfile
|
||||
Definition.build(default_gemfile, default_lockfile, unlock)
|
||||
end
|
||||
end
|
||||
|
||||
def locked_gems
|
||||
return @locked_gems if defined?(@locked_gems)
|
||||
if Bundler.default_lockfile.exist?
|
||||
lock = Bundler.read_file(Bundler.default_lockfile)
|
||||
@locked_gems = LockfileParser.new(lock)
|
||||
else
|
||||
@locked_gems = nil
|
||||
end
|
||||
end
|
||||
|
||||
def ruby_scope
|
||||
"#{Bundler.rubygems.ruby_engine}/#{Bundler.rubygems.config_map[:ruby_version]}"
|
||||
end
|
||||
|
||||
def user_bundle_path
|
||||
Pathname.new(Bundler.rubygems.user_home).join(".bundle")
|
||||
end
|
||||
|
||||
def home
|
||||
bundle_path.join("bundler")
|
||||
end
|
||||
|
||||
def install_path
|
||||
home.join("gems")
|
||||
end
|
||||
|
||||
def specs_path
|
||||
bundle_path.join("specifications")
|
||||
end
|
||||
|
||||
def cache
|
||||
bundle_path.join("cache/bundler")
|
||||
end
|
||||
|
||||
def user_cache
|
||||
user_bundle_path.join("cache")
|
||||
end
|
||||
|
||||
def root
|
||||
@root ||= begin
|
||||
default_gemfile.dirname.expand_path
|
||||
rescue GemfileNotFound
|
||||
bundle_dir = default_bundle_dir
|
||||
raise GemfileNotFound, "Could not locate Gemfile or .bundle/ directory" unless bundle_dir
|
||||
Pathname.new(File.expand_path("..", bundle_dir))
|
||||
end
|
||||
end
|
||||
|
||||
def app_config_path
|
||||
if ENV["BUNDLE_APP_CONFIG"]
|
||||
Pathname.new(ENV["BUNDLE_APP_CONFIG"]).expand_path(root)
|
||||
else
|
||||
root.join(".bundle")
|
||||
end
|
||||
end
|
||||
|
||||
def app_cache(custom_path = nil)
|
||||
path = custom_path || root
|
||||
path.join(settings.app_cache_path)
|
||||
end
|
||||
|
||||
def tmp(name = Process.pid.to_s)
|
||||
Pathname.new(Dir.mktmpdir(["bundler", name]))
|
||||
end
|
||||
|
||||
def rm_rf(path)
|
||||
FileUtils.remove_entry_secure(path) if path && File.exist?(path)
|
||||
end
|
||||
|
||||
def settings
|
||||
return @settings if defined?(@settings)
|
||||
@settings = Settings.new(app_config_path)
|
||||
rescue GemfileNotFound
|
||||
@settings = Settings.new(Pathname.new(".bundle").expand_path)
|
||||
end
|
||||
|
||||
# @return [Hash] Environment present before Bundler was activated
|
||||
def original_env
|
||||
ORIGINAL_ENV.clone
|
||||
end
|
||||
|
||||
# @deprecated Use `original_env` instead
|
||||
# @return [Hash] Environment with all bundler-related variables removed
|
||||
def clean_env
|
||||
env = original_env
|
||||
|
||||
if env.key?("BUNDLE_ORIG_MANPATH")
|
||||
env["MANPATH"] = env["BUNDLE_ORIG_MANPATH"]
|
||||
end
|
||||
|
||||
env.delete_if { |k, _| k[0, 7] == "BUNDLE_" }
|
||||
|
||||
if env.key?("RUBYOPT")
|
||||
env["RUBYOPT"] = env["RUBYOPT"].sub "-rbundler/setup", ""
|
||||
end
|
||||
|
||||
if env.key?("RUBYLIB")
|
||||
rubylib = env["RUBYLIB"].split(File::PATH_SEPARATOR)
|
||||
rubylib.delete(File.expand_path("..", __FILE__))
|
||||
env["RUBYLIB"] = rubylib.join(File::PATH_SEPARATOR)
|
||||
end
|
||||
|
||||
env
|
||||
end
|
||||
|
||||
def with_original_env
|
||||
with_env(original_env) { yield }
|
||||
end
|
||||
|
||||
def with_clean_env
|
||||
with_env(clean_env) { yield }
|
||||
end
|
||||
|
||||
def clean_system(*args)
|
||||
with_clean_env { Kernel.system(*args) }
|
||||
end
|
||||
|
||||
def clean_exec(*args)
|
||||
with_clean_env { Kernel.exec(*args) }
|
||||
end
|
||||
|
||||
def default_gemfile
|
||||
SharedHelpers.default_gemfile
|
||||
end
|
||||
|
||||
def default_lockfile
|
||||
SharedHelpers.default_lockfile
|
||||
end
|
||||
|
||||
def default_bundle_dir
|
||||
SharedHelpers.default_bundle_dir
|
||||
end
|
||||
|
||||
def system_bindir
|
||||
# Gem.bindir doesn't always return the location that Rubygems will install
|
||||
# system binaries. If you put '-n foo' in your .gemrc, Rubygems will
|
||||
# install binstubs there instead. Unfortunately, Rubygems doesn't expose
|
||||
# that directory at all, so rather than parse .gemrc ourselves, we allow
|
||||
# the directory to be set as well, via `bundle config bindir foo`.
|
||||
Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir
|
||||
end
|
||||
|
||||
def requires_sudo?
|
||||
return @requires_sudo if defined?(@requires_sudo_ran)
|
||||
|
||||
sudo_present = which "sudo" if settings.allow_sudo?
|
||||
|
||||
if sudo_present
|
||||
# the bundle path and subdirectories need to be writable for Rubygems
|
||||
# to be able to unpack and install gems without exploding
|
||||
path = bundle_path
|
||||
path = path.parent until path.exist?
|
||||
|
||||
# bins are written to a different location on OS X
|
||||
bin_dir = Pathname.new(Bundler.system_bindir)
|
||||
bin_dir = bin_dir.parent until bin_dir.exist?
|
||||
|
||||
# if any directory is not writable, we need sudo
|
||||
files = [path, bin_dir] | Dir[path.join("build_info/*").to_s] | Dir[path.join("*").to_s]
|
||||
sudo_needed = files.any? { |f| !File.writable?(f) }
|
||||
end
|
||||
|
||||
@requires_sudo_ran = true
|
||||
@requires_sudo = settings.allow_sudo? && sudo_present && sudo_needed
|
||||
end
|
||||
|
||||
def mkdir_p(path)
|
||||
if requires_sudo?
|
||||
sudo "mkdir -p '#{path}'" unless File.exist?(path)
|
||||
else
|
||||
SharedHelpers.filesystem_access(path, :write) do |p|
|
||||
FileUtils.mkdir_p(p)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def which(executable)
|
||||
if File.file?(executable) && File.executable?(executable)
|
||||
executable
|
||||
elsif paths = ENV["PATH"]
|
||||
quote = '"'.freeze
|
||||
paths.split(File::PATH_SEPARATOR).find do |path|
|
||||
path = path[1..-2] if path.start_with?(quote) && path.end_with?(quote)
|
||||
executable_path = File.expand_path(executable, path)
|
||||
return executable_path if File.file?(executable_path) && File.executable?(executable_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sudo(str)
|
||||
SUDO_MUTEX.synchronize do
|
||||
prompt = "\n\n" + <<-PROMPT.gsub(/^ {6}/, "").strip + " "
|
||||
Your user account isn't allowed to install to the system Rubygems.
|
||||
You can cancel this installation and run:
|
||||
|
||||
bundle install --path vendor/bundle
|
||||
|
||||
to install the gems into ./vendor/bundle/, or you can enter your password
|
||||
and install the bundled gems to Rubygems using sudo.
|
||||
|
||||
Password:
|
||||
PROMPT
|
||||
|
||||
`sudo -p "#{prompt}" #{str}`
|
||||
end
|
||||
end
|
||||
|
||||
def read_file(file)
|
||||
File.open(file, "rb", &:read)
|
||||
end
|
||||
|
||||
def load_marshal(data)
|
||||
Marshal.load(data)
|
||||
rescue => e
|
||||
raise MarshalError, "#{e.class}: #{e.message}"
|
||||
end
|
||||
|
||||
def load_gemspec(file, validate = false)
|
||||
@gemspec_cache ||= {}
|
||||
key = File.expand_path(file)
|
||||
@gemspec_cache[key] ||= load_gemspec_uncached(file, validate)
|
||||
# Protect against caching side-effected gemspecs by returning a
|
||||
# new instance each time.
|
||||
@gemspec_cache[key].dup if @gemspec_cache[key]
|
||||
end
|
||||
|
||||
def load_gemspec_uncached(file, validate = false)
|
||||
path = Pathname.new(file)
|
||||
# Eval the gemspec from its parent directory, because some gemspecs
|
||||
# depend on "./" relative paths.
|
||||
SharedHelpers.chdir(path.dirname.to_s) do
|
||||
contents = path.read
|
||||
spec = if contents[0..2] == "---" # YAML header
|
||||
eval_yaml_gemspec(path, contents)
|
||||
else
|
||||
eval_gemspec(path, contents)
|
||||
end
|
||||
return unless spec
|
||||
spec.loaded_from = path.expand_path.to_s
|
||||
Bundler.rubygems.validate(spec) if validate
|
||||
spec
|
||||
end
|
||||
end
|
||||
|
||||
def clear_gemspec_cache
|
||||
@gemspec_cache = {}
|
||||
end
|
||||
|
||||
def git_present?
|
||||
return @git_present if defined?(@git_present)
|
||||
@git_present = Bundler.which("git") || Bundler.which("git.exe")
|
||||
end
|
||||
|
||||
def reset!
|
||||
@definition = nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def eval_yaml_gemspec(path, contents)
|
||||
# If the YAML is invalid, Syck raises an ArgumentError, and Psych
|
||||
# raises a Psych::SyntaxError. See psyched_yaml.rb for more info.
|
||||
Gem::Specification.from_yaml(contents)
|
||||
rescue YamlLibrarySyntaxError, ArgumentError, Gem::EndOfYAMLException, Gem::Exception
|
||||
eval_gemspec(path, contents)
|
||||
end
|
||||
|
||||
def eval_gemspec(path, contents)
|
||||
eval(contents, TOPLEVEL_BINDING, path.expand_path.to_s)
|
||||
rescue ScriptError, StandardError => e
|
||||
original_line = e.backtrace.find { |line| line.include?(path.to_s) }
|
||||
msg = String.new
|
||||
msg << "There was a #{e.class} while loading #{path.basename}: \n#{e.message}"
|
||||
msg << " from\n #{original_line}" if original_line
|
||||
msg << "\n"
|
||||
|
||||
if e.is_a?(LoadError) && RUBY_VERSION >= "1.9"
|
||||
msg << "\nDoes it try to require a relative path? That's been removed in Ruby 1.9."
|
||||
end
|
||||
|
||||
raise GemspecError, msg
|
||||
end
|
||||
|
||||
def configure_gem_home_and_path
|
||||
blank_home = ENV["GEM_HOME"].nil? || ENV["GEM_HOME"].empty?
|
||||
if settings[:disable_shared_gems]
|
||||
ENV["GEM_PATH"] = ""
|
||||
elsif blank_home || Bundler.rubygems.gem_dir != bundle_path.to_s
|
||||
possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path]
|
||||
paths = possibles.flatten.compact.uniq.reject(&:empty?)
|
||||
ENV["GEM_PATH"] = paths.join(File::PATH_SEPARATOR)
|
||||
end
|
||||
|
||||
configure_gem_home
|
||||
bundle_path
|
||||
end
|
||||
|
||||
def configure_gem_home
|
||||
# TODO: This mkdir_p is only needed for JRuby <= 1.5 and should go away (GH #602)
|
||||
begin
|
||||
FileUtils.mkdir_p bundle_path.to_s
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
ENV["GEM_HOME"] = File.expand_path(bundle_path, root)
|
||||
Bundler.rubygems.clear_paths
|
||||
end
|
||||
|
||||
def upgrade_lockfile
|
||||
lockfile = default_lockfile
|
||||
return unless lockfile.exist? && lockfile.read(3) == "---"
|
||||
Bundler.ui.warn "Detected Gemfile.lock generated by 0.9, deleting..."
|
||||
lockfile.rmtree
|
||||
end
|
||||
|
||||
# @param env [Hash]
|
||||
def with_env(env)
|
||||
backup = ENV.to_hash
|
||||
ENV.replace(env)
|
||||
yield
|
||||
ensure
|
||||
ENV.replace(backup)
|
||||
end
|
||||
end
|
||||
end
|
9
test/examples/rust/Cargo.toml
Normal file
9
test/examples/rust/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "rust"
|
||||
version = "0.1.0"
|
||||
authors = ["Andika Demas Riyandi <andika.riyan@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
3
test/examples/rust/src/main.rs
Normal file
3
test/examples/rust/src/main.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
8
test/examples/shell/foo.sh
Executable file
8
test/examples/shell/foo.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
hello() {
|
||||
echo "Hello"
|
||||
}
|
||||
|
||||
hello
|
4
test/examples/terraform/main.tf
Normal file
4
test/examples/terraform/main.tf
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
resource "my_resource" "xxx" {
|
||||
option = [1, 2, 3]
|
||||
}
|
4
test/examples/terraform/two.tf
Normal file
4
test/examples/terraform/two.tf
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
resource "other_resource" "xxx" {
|
||||
xxx = "xxx"
|
||||
}
|
82
test/treefmt.toml
Normal file
82
test/treefmt.toml
Normal file
@ -0,0 +1,82 @@
|
||||
# One CLI to format the code tree - https://github.com/numtide/treefmt
|
||||
|
||||
[formatter.python]
|
||||
command = "black"
|
||||
includes = ["*.py"]
|
||||
|
||||
[formatter.elm]
|
||||
command = "elm-format"
|
||||
options = ["--yes"]
|
||||
includes = ["*.elm"]
|
||||
|
||||
[formatter.go]
|
||||
command = "gofmt"
|
||||
options = ["-w"]
|
||||
includes = ["*.go"]
|
||||
|
||||
[formatter.haskell]
|
||||
command = "ormolu"
|
||||
options = [
|
||||
"--ghc-opt", "-XBangPatterns",
|
||||
"--ghc-opt", "-XPatternSynonyms",
|
||||
"--ghc-opt", "-XTypeApplications",
|
||||
"--mode", "inplace",
|
||||
"--check-idempotence",
|
||||
]
|
||||
includes = ["*.hs"]
|
||||
excludes = ["examples/haskell/"]
|
||||
|
||||
[formatter.nix]
|
||||
command = "nixpkgs-fmt"
|
||||
includes = ["*.nix"]
|
||||
# Act as an example on how to exclude specific files
|
||||
excludes = ["examples/nix/sources.nix"]
|
||||
|
||||
[formatter.ruby]
|
||||
command = "rufo"
|
||||
options = ["-x"]
|
||||
includes = ["*.rb"]
|
||||
|
||||
[formatter.prettier]
|
||||
command = "prettier"
|
||||
options = ["--write"]
|
||||
includes = [
|
||||
"*.css",
|
||||
"*.html",
|
||||
"*.js",
|
||||
"*.json",
|
||||
"*.jsx",
|
||||
"*.md",
|
||||
"*.mdx",
|
||||
"*.scss",
|
||||
"*.ts",
|
||||
"*.yaml",
|
||||
]
|
||||
excludes = ["CHANGELOG.md"]
|
||||
|
||||
[formatter.rust]
|
||||
command = "rustfmt"
|
||||
options = ["--edition", "2018"]
|
||||
includes = ["*.rs"]
|
||||
|
||||
[formatter.shell]
|
||||
command = "/bin/sh"
|
||||
options = [
|
||||
"-euc",
|
||||
"""
|
||||
# First lint all the scripts
|
||||
shellcheck "$@"
|
||||
|
||||
# Then format them
|
||||
shfmt -i 2 -s -w "$@"
|
||||
""",
|
||||
"--", # bash swallows the second argument when using -c
|
||||
]
|
||||
includes = ["*.sh"]
|
||||
|
||||
[formatter.terraform]
|
||||
# Careful, only terraform 1.3.0 or later accept a list of files.
|
||||
# see https://github.com/numtide/treefmt/issues/97
|
||||
command = "terraform"
|
||||
options = ["fmt"]
|
||||
includes = ["*.tf"]
|
Loading…
Reference in New Issue
Block a user