Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 189 additions & 0 deletions .github/workflows/homebrew-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
name: Release

on:
push:
tags: 'v*'

permissions:
contents: write

defaults:
run:
shell: bash

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: macos-14
arch: arm64
platform: apple-darwin
- os: ubuntu-latest
arch: x86_64
platform: linux
- os: windows-latest
arch: x86_64
platform: windows

name: build-${{ matrix.arch }}-${{ matrix.platform }}
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Setup Haskell
uses: haskell-actions/setup@v2
with:
ghc-version: '9.4.4'
cabal-version: '3.10'

- name: Install hpack
run: cabal install hpack --overwrite-policy=always

- name: Generate cabal files
run: |
(cd avh4-lib && hpack)
(cd elm-format-markdown && hpack)
(cd elm-format-lib && hpack)
(cd elm-format-test-lib && hpack)
hpack

- name: Generate Build_elm_format
run: |
mkdir -p generated
GIT_DESC=$(git describe --tags --always)
printf 'module Build_elm_format where\n\ngitDescribe :: String\ngitDescribe = "%s"\n' "$GIT_DESC" > generated/Build_elm_format.hs
cat generated/Build_elm_format.hs

- name: Build
run: |
cabal update
cabal build exe:elm-format -O2

- name: Package binary (unix)
if: runner.os != 'Windows'
run: |
BIN=$(cabal -v0 list-bin elm-format)
if [ ! -f "$BIN" ]; then
BIN=$(find dist-newstyle -path '*/elm-format/elm-format' -type f | head -1)
fi
mkdir staging
cp "$BIN" staging/elm-format
strip staging/elm-format
TAG=${GITHUB_REF_NAME}
TARBALL="elm-format-${TAG}-${{ matrix.arch }}-${{ matrix.platform }}.tar.gz"
tar -czf "$TARBALL" -C staging elm-format
echo "TARBALL=$TARBALL" >> $GITHUB_ENV

- name: Package binary (windows)
if: runner.os == 'Windows'
shell: bash
run: |
BIN=$(cabal -v0 list-bin elm-format)
if [ ! -f "$BIN" ]; then
BIN=$(find dist-newstyle -name elm-format.exe -type f | head -1)
fi
mkdir staging
cp "$BIN" staging/elm-format.exe
TAG=${GITHUB_REF_NAME}
ZIPFILE="elm-format-${TAG}-${{ matrix.arch }}-${{ matrix.platform }}.zip"
(cd staging && 7z a "../$ZIPFILE" elm-format.exe)
echo "TARBALL=$ZIPFILE" >> $GITHUB_ENV

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: elm-format-${{ matrix.arch }}-${{ matrix.platform }}
path: ${{ env.TARBALL }}

release:
needs: build
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.tag.outputs.tag }}
steps:
- name: Get tag
id: tag
run: echo "tag=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT

- name: Download artifacts
uses: actions/download-artifact@v4
with:
merge-multiple: true

- name: List artifacts
run: ls -lh elm-format-*

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: 'elm-format-*'
generate_release_notes: true

update-tap:
needs: release
runs-on: ubuntu-latest
steps:
- name: Generate formula
run: |
TAG=${{ needs.release.outputs.tag }}
VERSION=${TAG#v}
REPO=${{ github.repository }}

ARM64_MAC_URL="https://github.com/${REPO}/releases/download/${TAG}/elm-format-${TAG}-arm64-apple-darwin.tar.gz"
LINUX_URL="https://github.com/${REPO}/releases/download/${TAG}/elm-format-${TAG}-x86_64-linux.tar.gz"

# Wait for release assets to be available
sleep 10

curl -sfL "$ARM64_MAC_URL" -o arm64-mac.tar.gz
curl -sfL "$LINUX_URL" -o x86_64-linux.tar.gz
ARM64_MAC_SHA=$(sha256sum arm64-mac.tar.gz | cut -d' ' -f1)
LINUX_SHA=$(sha256sum x86_64-linux.tar.gz | cut -d' ' -f1)

cat > elm-format.rb <<RUBY
class ElmFormat < Formula
desc "Source code formatter for Elm (with teleport-imports)"
homepage "https://github.com/${REPO}"
version "${VERSION}"
license "BSD-3-Clause"

on_macos do
if Hardware::CPU.arm?
url "${ARM64_MAC_URL}"
sha256 "${ARM64_MAC_SHA}"
end
end

on_linux do
url "${LINUX_URL}"
sha256 "${LINUX_SHA}"
end

def install
bin.install "elm-format"
end

test do
assert_match "elm-format", shell_output("#{bin}/elm-format --help")
end
end
RUBY

sed -i 's/^ //' elm-format.rb

echo "============================================"
echo "Generated formula:"
echo "============================================"
cat elm-format.rb

- name: Upload formula artifact
uses: actions/upload-artifact@v4
with:
name: homebrew-formula
path: elm-format.rb
26 changes: 26 additions & 0 deletions Formula/elm-format.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class ElmFormat < Formula
desc "Source code formatter for Elm (with teleport-imports)"
homepage "https://github.com/perkee/elm-format"
version "0.8.7-teleport"
license "BSD-3-Clause"

on_macos do
if Hardware::CPU.arm?
url "https://github.com/perkee/elm-format/releases/download/v0.8.7-teleport/elm-format-v0.8.7-teleport-arm64-apple-darwin.tar.gz"
sha256 "bc6a2ca8eec8fda0d2164de75d514df6e291a12de5eed92fd13aafb4ec544c41"
end
end

on_linux do
url "https://github.com/perkee/elm-format/releases/download/v0.8.7-teleport/elm-format-v0.8.7-teleport-x86_64-linux.tar.gz"
sha256 "12eb671c8f3345fb2e302b30cfe7724dffde06ba02063e7583b79529ab9363b3"
end

def install
bin.install "elm-format"
end

test do
assert_match "elm-format", shell_output("#{bin}/elm-format --help")
end
end
61 changes: 46 additions & 15 deletions elm-format-lib/src/Parse/Module.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import qualified Data.Indexed as I
import ElmVersion
import Parse.IParser
import Parse.Whitespace
import qualified Reporting.Annotation as A
import Reporting.Annotation (Located)


Expand All @@ -31,25 +32,25 @@ elmModule elmVersion =
, (,) <$> addLocation (return Nothing) <*> return []
]
(preImportComments, imports', postImportComments) <- imports elmVersion
topLevels <-
fmap I.Fix $
addLocation $
fmap TopLevel $
(startPos, (allDecls, displacedImports), endPos) <-
located $
do
decls <- topLevel $ Decl.declaration elmVersion
(decls, extraImports) <- topLevelWithImports elmVersion (Decl.declaration elmVersion)
trailingComments <-
(++)
<$> option [] freshLine
<*> option [] spaces
eof
return ((map BodyComment postImportComments) ++ decls ++ (map BodyComment trailingComments))
return ((map BodyComment postImportComments) ++ decls ++ (map BodyComment trailingComments), extraImports)
let topLevels = I.Fix $ A.at startPos endPos $ TopLevel allDecls
let mergedImports = mergeDisplacedImports imports' displacedImports

return $
Module.Module
preModule
h
docs
(C (preDocsComments ++ postDocsComments ++ preImportComments) imports')
(C (preDocsComments ++ postDocsComments ++ preImportComments) mergedImports)
topLevels


Expand All @@ -67,6 +68,43 @@ freshDef entry =
return $ (map BodyComment comments) ++ [decl]


topLevelWithImports :: ElmVersion -> IParser a -> IParser ([TopLevelStructure a], [Module.UserImport])
topLevelWithImports elmVersion entry =
do firstEntry <- option ([], []) $
(\d -> ([d], [])) <$> Decl.topLevelStructure entry
restEntries <- many (freshDefOrImport elmVersion entry)
let (firstDecls, firstImports) = firstEntry
let (restDeclss, restImportss) = unzip restEntries
return (firstDecls ++ concat restDeclss, firstImports ++ concat restImportss)


freshDefOrImport :: ElmVersion -> IParser a -> IParser ([TopLevelStructure a], [Module.UserImport])
freshDefOrImport elmVersion entry =
commitIf (freshLine >> (letter <|> char '_')) $
do comments <- freshLine
choice
[ do imp <- import' elmVersion
return (map BodyComment comments, [imp])
, do decl <- Decl.topLevelStructure entry
return ((map BodyComment comments) ++ [decl], [])
]


mergeDisplacedImports :: Map [UppercaseIdentifier] (C1 'BeforeTerm ImportMethod) -> [Module.UserImport] -> Map [UppercaseIdentifier] (C1 'BeforeTerm ImportMethod)
mergeDisplacedImports existing displaced =
foldl step existing displaced
where
step m (C pre name, method) = insertWith mergeImport name (C pre method) m


mergeImport :: C1 'BeforeTerm ImportMethod -> C1 'BeforeTerm ImportMethod -> C1 'BeforeTerm ImportMethod
mergeImport (C comments1 import1) (C comments2 import2) =
C (comments1 ++ comments2) $
Module.ImportMethod
(Module.alias import1 Control.Applicative.<|> Module.alias import2)
(mergeC2 (mergeListing mergeDetailedListing) (Module.exposedVars import1) (Module.exposedVars import2))


moduleDecl :: ElmVersion -> IParser (Maybe Module.Header)
moduleDecl elmVersion =
choice
Expand Down Expand Up @@ -151,16 +189,9 @@ mergeDetailedListing left right =
imports :: ElmVersion -> IParser (Comments, Map [UppercaseIdentifier] (C1 'BeforeTerm ImportMethod), Comments)
imports elmVersion =
let
merge :: C1 'BeforeTerm ImportMethod -> C1 'BeforeTerm ImportMethod -> C1 'BeforeTerm ImportMethod
merge (C comments1 import1) (C comments2 import2) =
C (comments1 ++ comments2) $
Module.ImportMethod
(Module.alias import1 Control.Applicative.<|> Module.alias import2)
(mergeC2 (mergeListing mergeDetailedListing) (Module.exposedVars import1) (Module.exposedVars import2))

step (comments, m, finalComments) (((C pre name), method), post) =
( comments ++ finalComments
, insertWith merge name (C pre method) m
, insertWith mergeImport name (C pre method) m
, post
)

Expand Down
61 changes: 61 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading