diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d5d33115bf9..5ce889eea19 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: '' -labels: '' +labels: ["type: bug", "needs triage"] assignees: '' --- @@ -14,11 +14,9 @@ A clear and concise description of what the bug is. Steps to reproduce the behavior: ``` -$ cabal v2-build ... +$ cabal build ... ``` -Please use version-prefixed commands (e.g. `v2-build` or `v1-build`) to avoid ambiguity. - **Expected behavior** A clear and concise description of what you expected to happen. @@ -28,3 +26,4 @@ A clear and concise description of what you expected to happen. **Additional context** Add any other context about the problem here. + diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 00000000000..535e725d23a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,15 @@ +--- +name: Documentation +about: Report missing, stale, or inaccurate documentation +title: '' +labels: ["documentation"] +assignees: '' + +--- + +**What is wrong with the docs?** +Describe what you were searching. Was documentation not there? Was it outdated? Was it not clear? + +**Additional context** +Add any other relevant context here. + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000000..bf5a06f0c02 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,15 @@ +--- +name: Feature request +about: Suggest a new feature +title: '' +labels: ["type: enhancement"] +assignees: '' + +--- + +**Describe the feature request** +A simple description of what you would like to be added to Cabal. + +**Additional context** +What made you ask for this functionality? How would the feature benefit you and other users? Add any other relevant context about the request here. + diff --git a/.github/ISSUE_TEMPLATE/user_question.md b/.github/ISSUE_TEMPLATE/user_question.md new file mode 100644 index 00000000000..064e84ea441 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/user_question.md @@ -0,0 +1,23 @@ +--- +name: Question +about: Ask a question to the developers +title: '' +labels: ["type: user-question"] +assignees: '' + +--- + +Two great places to ask questions are [Haskell Matrix](https://matrix.to/#/#haskell:matrix.org) (online chat) and [Haskell Discourse](https://discourse.haskell.org). There are many experienced programmers there who can quickly help you with using Cabal and `cabal-install`. + +If you did not find an answer to your question, fill in this template. + +**What is your question?** +State your question in simple terms. What were you attempting to do? What have you have tried? + +**System information** + - Operating system + - `cabal`, `ghc` versions + +**Additional context** +Add any other relevant context here. + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..b3b2315d909 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# From: +# - https://github.com/haskell/hackage-server +# - https://github.com/rhysd/actionlint/issues/228#issuecomment-1272493095 +# - https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot + +# Set update schedule for GitHub Actions + +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "weekly" diff --git a/.github/mergify.yml b/.github/mergify.yml new file mode 100644 index 00000000000..b0dbe1033b8 --- /dev/null +++ b/.github/mergify.yml @@ -0,0 +1,85 @@ +pull_request_rules: + + # implementing PR delay logic: apply a label after 2 days of inactivity + # the label will allow Mergify to merge (see #8442, #8448) + - actions: + label: + add: + - merge delay passed + name: Wait for 2 days before validating merge + conditions: + - updated-at<2 days ago + - or: + - label=merge me + - label=squash+merge me + - label=merge+no rebase + - '#approved-reviews-by>=2' + + # rebase+merge strategy + - actions: + queue: + name: default + # Merge into master with a merge commit + method: merge + # Update the pr branch with rebase, so the history is clean + update_method: rebase + name: Put pull requests in the rebase+merge queue + conditions: + - base=master + - label=merge me + - label=merge delay passed + - '#approved-reviews-by>=2' + + # merge+squash strategy + - actions: + queue: + name: default + method: squash + # both update methods get absorbed by the squash, so we use the most + # reliable + update_method: merge + name: Put pull requests in the squash+merge queue + conditions: + - base=master + - label=squash+merge me + - label=merge delay passed + - '#approved-reviews-by>=2' + + # merge+no rebase strategy + - actions: + merge: + method: merge + name: Merge "merge+no rebase" pull requests directly (without a queue) + conditions: + - base=master + - label=merge+no rebase + - label=merge delay passed + - '#approved-reviews-by>=2' + + # rebase+merge strategy for backports: require 1 approver instead of 2 + - actions: + queue: + name: default + # Merge with a merge commit + method: merge + # Update the pr branch with rebase, so the history is clean + update_method: rebase + name: Put backports in the rebase+merge queue + conditions: + - label=merge me + - base!=master + - body~=backport + - '#approved-reviews-by>=1' + + # backports should be labeled as such + - actions: + label: + add: + - backport + name: Label backports as such + conditions: + - body~=automatic backport + +queue_rules: + - name: default + update_bot_account: Mikolaj diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..8b36d183025 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,22 @@ +Please read [Github PR Conventions](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#github-pull-request-conventions) and then fill in *one* of these two templates. + +--- + +**Template Α: This PR modifies `cabal` behaviour** + +Include the following checklist in your PR: + +* [ ] Patches conform to the [coding conventions](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#other-conventions). +* [ ] Any changes that could be relevant to users [have been recorded in the changelog](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#changelog). +* [ ] The documentation has been updated, if necessary. +* [ ] [Manual QA notes](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#qa-notes) have been included. +* [ ] Tests have been added. (*Ask for help if you don’t know how to write them! Ask for an exemption if tests are too complex for too little coverage!*) + +--- + +**Template Β: This PR does not modify `cabal` behaviour (documentation, tests, refactoring, etc.)** + +Include the following checklist in your PR: + +* [ ] Patches conform to the [coding conventions](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#other-conventions). + diff --git a/.github/workflows/bootstrap.skip.yml b/.github/workflows/bootstrap.skip.yml new file mode 100644 index 00000000000..4a92ddaa0c6 --- /dev/null +++ b/.github/workflows/bootstrap.skip.yml @@ -0,0 +1,39 @@ +name: Bootstrap Skip + +# This Workflow is special and contains a workaround for a known limitation of GitHub CI. +# +# The problem: We don't want to run the "bootstrap" jobs on PRs which contain only changes +# to the docs, since these jobs take a long time to complete without providing any benefit. +# We therefore use path-filtering in the workflow triggers for the bootstrap jobs, namely +# "paths-ignore: doc/**". But the "Bootstrap post job" is a required job, therefore a PR cannot +# be merged unless the "Bootstrap post job" completes succesfully, which it doesn't do if we +# filter it out. +# +# The solution: We use a second job with the same name which always returns the exit code 0. +# The logic implemented for "required" workflows accepts if 1) at least one job with that name +# runs through, AND 2) If multiple jobs of that name exist, then all jobs of that name have to +# finish successfully. +on: + push: + paths: + - 'doc/**' + - '**/README.md' + - 'CONTRIBUTING.md' + branches: + - master + pull_request: + paths: + - 'doc/**' + - '**/README.md' + - 'CONTRIBUTING.md' + release: + types: + - created + +jobs: + bootstrap-post-job: + if: always() + name: Bootstrap post job + runs-on: ubuntu-latest + steps: + - run: exit 0 diff --git a/.github/workflows/bootstrap.yml b/.github/workflows/bootstrap.yml index 533729d8faa..03dafc3f59d 100644 --- a/.github/workflows/bootstrap.yml +++ b/.github/workflows/bootstrap.yml @@ -1,66 +1,96 @@ -# This file is auto-generated -# -# To regenerate it run -# -# make github-actions -# name: Bootstrap + +# See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency. +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +# Note: This workflow file contains the required job "Bootstrap post job". We are using path filtering +# here to ignore PRs which only change documentation. This can cause a problem, see the workflow file +# "bootstrap.skip.yml" for a description of the problem and the solution provided in that file. on: push: + paths-ignore: + - 'doc/**' + - '**/README.md' + - 'CONTRIBUTING.md' branches: - master pull_request: + paths-ignore: + - 'doc/**' + - '**/README.md' + - 'CONTRIBUTING.md' release: types: - created jobs: - boostrap-linux: - name: Bootstrap on Linux - runs-on: ubuntu-18.04 + bootstrap: + strategy: + matrix: + os: [ubuntu-latest] + ghc: ["8.10.7", "9.0.2", "9.2.7", "9.4.4"] + include: + - os: macos-latest + ghc: "9.2.7" + name: Bootstrap ${{ matrix.os }} ghc-${{ matrix.ghc }} + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 - - name: bootstrap.py - run: | - ghcup config set cache true - ghcup install ghc 8.10.7 - python3 bootstrap/bootstrap.py -w $(ghcup whereis ghc 8.10.7) -d bootstrap/linux-8.10.7.json - - - name: Smoke test - run: | - _build/bin/cabal --version - - - uses: actions/upload-artifact@v2 + - uses: actions/cache@v3 + name: Cache the downloads + id: bootstrap-cache with: - name: cabal-linux-bootstrapped - path: _build/artifacts/* + path: "/home/runner/work/cabal/cabal/_build" + key: bootstrap-${{ runner.os }}-${{ matrix.ghc }}-20221115-${{ github.sha }} + restore-keys: bootstrap-${{ runner.os }}-${{ matrix.ghc }}-20221115- - boostrap-macos: - name: Bootstrap on macOS - runs-on: macos-latest - steps: - - name: Install GHC + - uses: actions/checkout@v4 + # See https://github.com/haskell/cabal/pull/8739 + - name: Sudo chmod to permit ghcup to update its cache run: | - cd $(mktemp -d) - curl -sLO "https://downloads.haskell.org/~ghc/8.10.7/ghc-8.10.7-x86_64-apple-darwin.tar.xz" - tar -xJf ghc-*.tar.xz - cd ghc-* - ./configure --prefix=/opt/ghc/8.10.7 - sudo make install - - uses: actions/checkout@v2 - - # We use linux dependencies + if [[ "${{ runner.os }}" == "Linux" ]]; then + sudo ls -lah /usr/local/.ghcup/cache + sudo mkdir -p /usr/local/.ghcup/cache + sudo ls -lah /usr/local/.ghcup/cache + sudo chown -R $USER /usr/local/.ghcup + sudo chmod -R 777 /usr/local/.ghcup + fi - name: bootstrap.py run: | + GHC_VERSION=${{ matrix.ghc }} + ghcup --version ghcup config set cache true - ghcup install ghc 8.10.7 - python3 bootstrap/bootstrap.py -w $(ghcup whereis ghc 8.10.7) -d bootstrap/linux-8.10.7.json + ghcup install ghc $GHC_VERSION + + # Fetch the bootstrap sources (we use linux dependencies also on macos) + python3 bootstrap/bootstrap.py -w $(ghcup whereis ghc $GHC_VERSION) -d bootstrap/linux-$GHC_VERSION.json fetch + + # Bootstrap using the bootstrap sources + python3 bootstrap/bootstrap.py -w $(ghcup whereis ghc $GHC_VERSION) --bootstrap-sources bootstrap-sources.tar.gz - name: Smoke test run: | _build/bin/cabal --version - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: - name: cabal-macos-bootstrapped + name: cabal-${{ matrix.os }}-${{ matrix.ghc }}-bootstrapped path: _build/artifacts/* + + # We use this job as a summary of the workflow + # It will fail if any of the previous jobs does it + # This way we can use it exclusively in branch protection rules + # and abstract away the concrete jobs of the workflow, including their names + bootstrap-post-job: + if: always() + name: Bootstrap post job + runs-on: ubuntu-latest + # IMPORTANT! Any job added to the workflow should be added here too + needs: [bootstrap] + + steps: + - run: | + echo "jobs info: ${{ toJSON(needs) }}" + - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + run: exit 1 diff --git a/.github/workflows/changelogs.yml b/.github/workflows/changelogs.yml new file mode 100644 index 00000000000..c0ec46a6d1b --- /dev/null +++ b/.github/workflows/changelogs.yml @@ -0,0 +1,65 @@ +name: Assorted + +on: + push: + branches: + - master + paths: + - 'changelog.d/*' + - '.github/workflows/changelogs.yml' + pull_request: + paths: + - 'changelog.d/*' + - '.github/workflows/changelogs.yml' + release: + types: + - created + +defaults: + run: + shell: bash + +jobs: + build: + name: Changelogs + runs-on: ubuntu-latest + + steps: + - name: Set PATH + # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#adding-a-system-path + run: | + echo "$HOME/.cabal/bin" >> $GITHUB_PATH + - uses: actions/cache@v3 + with: + path: ~/.cabal/store + key: linux-store-changelogs + # See https://github.com/haskell/cabal/pull/8739 + - name: Sudo chmod to permit ghcup to update its cache + run: | + if [[ "${{ runner.os }}" == "Linux" ]]; then + sudo mkdir -p /usr/local/.ghcup/cache + sudo chown -R $USER /usr/local/.ghcup + sudo chmod -R 777 /usr/local/.ghcup + fi + - name: ghcup + run: | + ghcup config set cache true + ghcup install ghc recommended + ghcup set ghc recommended + - name: Update Hackage index + run: cabal v2-update + # Cannot install it from tarball due to + # https://github.com/haskell/cabal/issues/7360 + - uses: actions/checkout@v4 + with: + repository: "fgaz/changelog-d" + path: "changelog-d" + - name: Install changelog-d + run: | + pushd changelog-d + cabal v2-install + popd + - uses: actions/checkout@v4 + - name: Run changelog-d + run: | + changelog-d changelog.d diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 00000000000..84e639e7d1c --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,19 @@ +name: Formatting + +on: + pull_request: + push: + branches: ["master"] + +jobs: + fourmolu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: haskell-actions/run-fourmolu@v9 + with: + version: "0.12.0.0" + pattern: | + Cabal/**/*.hs + Cabal-syntax/**/*.hs + cabal-install/**/*.hs diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000000..1bae4d3d71b --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,18 @@ +name: Linting + +on: + pull_request: + push: + +jobs: + hlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: haskell-actions/hlint-setup@v2 + with: + version: "3.5" + - uses: haskell-actions/hlint-run@v2 + with: + path: "." + fail-on: suggestion diff --git a/.github/workflows/quick-jobs.yml b/.github/workflows/quick-jobs.yml index 2da8bd031ec..9426ed79724 100644 --- a/.github/workflows/quick-jobs.yml +++ b/.github/workflows/quick-jobs.yml @@ -1,10 +1,10 @@ -# This file is auto-generated -# -# To regenerate it run -# -# make github-actions -# name: Quick jobs + +# See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency. +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + on: push: branches: @@ -17,71 +17,78 @@ on: jobs: meta: name: Meta checks - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest # This job is not run in a container, any recent GHC should be fine steps: - name: Set PATH # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#adding-a-system-path run: | echo "$HOME/.cabal/bin" >> $GITHUB_PATH - - uses: actions/cache@v1 + - uses: actions/cache@v3 with: path: ~/.cabal/store key: linux-store-meta + # See https://github.com/haskell/cabal/pull/8739 + - name: Sudo chmod to permit ghcup to update its cache + run: | + if [[ "${{ runner.os }}" == "Linux" ]]; then + sudo ls -lah /usr/local/.ghcup/cache + sudo mkdir -p /usr/local/.ghcup/cache + sudo ls -lah /usr/local/.ghcup/cache + sudo chown -R $USER /usr/local/.ghcup + sudo chmod -R 777 /usr/local/.ghcup + fi - name: ghcup run: | + ghcup --version ghcup config set cache true ghcup install ghc recommended ghcup set ghc recommended - name: Update Hackage index run: cabal v2-update - name: Install alex - run: cabal v2-install alex --constraint='alex ==3.2.6' - - uses: actions/checkout@v2 + run: cabal v2-install alex --constraint='alex ==3.2.7.3' + - uses: actions/checkout@v4 - name: Regenerate files run: | make -B lexer make -B spdx make -B templates - make -B github-actions - make -B cabal-install-cabal - name: Check that diff is clean run: | git status > /dev/null git diff-files -p --exit-code doctest: name: Doctest Cabal - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - name: Set PATH run: | echo "$HOME/.cabal/bin" >> $GITHUB_PATH - - name: Install cabal-env - run: | - mkdir -p $HOME/.cabal/bin - curl -sL https://github.com/phadej/cabal-extras/releases/download/preview-20191225/cabal-env-snapshot-20191225-x86_64-linux.xz > cabal-env.xz - echo "1b567d529c5f627fd8c956e57ae8f0d9f11ee66d6db34b7fb0cb1c370b4edf01 cabal-env.xz" | sha256sum -c - - xz -d < cabal-env.xz > $HOME/.cabal/bin/cabal-env - rm -f cabal-env.xz - chmod a+x $HOME/.cabal/bin/cabal-env - - uses: actions/cache@v1 + - uses: actions/cache@v3 with: path: ~/.cabal/store key: linux-store-doctest + # See https://github.com/haskell/cabal/pull/8739 + - name: Sudo chmod to permit ghcup to update its cache + run: | + if [[ "${{ runner.os }}" == "Linux" ]]; then + sudo ls -lah /usr/local/.ghcup/cache + sudo mkdir -p /usr/local/.ghcup/cache + sudo ls -lah /usr/local/.ghcup/cache + sudo chown -R $USER /usr/local/.ghcup + sudo chmod -R 777 /usr/local/.ghcup + fi - name: ghcup run: | + ghcup --version ghcup config set cache true - ghcup install ghc recommended - ghcup set ghc recommended + ghcup install ghc --set recommended + ghcup install cabal --set latest - name: Update Hackage index run: cabal v2-update + - uses: actions/checkout@v4 - name: Install doctest - run: cabal v2-install doctest - - name: Install libraries - run: | - cabal-env --transitive QuickCheck - cabal-env array bytestring containers deepseq directory filepath pretty process time binary unix text parsec mtl - cat $HOME/.ghc/*/environments/default - - uses: actions/checkout@v2 + run: make doctest-install - name: Doctest run: make doctest diff --git a/.github/workflows/users-guide.yml b/.github/workflows/users-guide.yml new file mode 100644 index 00000000000..ac6d65baedb --- /dev/null +++ b/.github/workflows/users-guide.yml @@ -0,0 +1,79 @@ +# Adapted from agda/agda/.github/workflows/user-manual.yml by Andreas, 2021-09-11 + +name: Assorted + +# See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency. +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +on: + push: + branches: + - master + paths: + - 'doc/Makefile' + - 'doc/pyproject.toml' + - 'doc/requirements.in' + - 'doc/requirements.txt' + - 'doc/*.inc' + - 'doc/*.py' + - 'doc/*.rst' + - 'doc/**/*.json' + - '.github/workflows/users-guide.yml' + pull_request: + paths: + - 'doc/Makefile' + - 'doc/pyproject.toml' + - 'doc/requirements.in' + - 'doc/requirements.txt' + - 'doc/*.inc' + - 'doc/*.py' + - 'doc/*.rst' + - 'doc/**/*.json' + - '.github/workflows/users-guide.yml' + release: + types: + - created + +defaults: + run: + shell: bash + +jobs: + build: + name: Users guide + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.10'] + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + # Subsumed by make users-guide + # - name: Install dependencies + # run: | + # pip install -r doc/requirements.txt + + - name: Build User's Guide in HTML + run: | + make SPHINX_HTML_OUTDIR=html users-guide + + - uses: actions/upload-artifact@v3 + with: + name: users-guide-html + path: html/ + + - name: Check security of requirements.txt + env: + SKJOLD_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + make -C doc check-requirements diff --git a/.github/workflows/validate.skip.yml b/.github/workflows/validate.skip.yml new file mode 100644 index 00000000000..e5cd47e284a --- /dev/null +++ b/.github/workflows/validate.skip.yml @@ -0,0 +1,39 @@ +name: Validate Skip + +# This Workflow is special and contains a workaround for a known limitation of GitHub CI. +# +# The problem: We don't want to run the "validate" jobs on PRs which contain only changes +# to the docs, since these jobs take a long time to complete without providing any benefit. +# We therefore use path-filtering in the workflow triggers for the validate jobs, namely +# "paths-ignore: doc/**". But the "Validate post job" is a required job, therefore a PR cannot +# be merged unless the "Validate post job" completes succesfully, which it doesn't do if we +# filter it out. +# +# The solution: We use a second job with the same name which always returns the exit code 0. +# The logic implemented for "required" workflows accepts if 1) at least one job with that name +# runs through, AND 2) If multiple jobs of that name exist, then all jobs of that name have to +# finish successfully. +on: + push: + paths: + - 'doc/**' + - '**/README.md' + - 'CONTRIBUTING.md' + branches: + - master + pull_request: + paths: + - 'doc/**' + - '**/README.md' + - 'CONTRIBUTING.md' + release: + types: + - created + +jobs: + validate-post-job: + if: always() + name: Validate post job + runs-on: ubuntu-latest + steps: + - run: exit 0 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 00000000000..78652b10af7 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,346 @@ +name: Validate + +# We use bash as default even in windows +# to try keep the workflow as uniform as possible +defaults: + run: + shell: bash + +# See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency. +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +# Note: This workflow file contains the required job "Validate post job". We are using path filtering +# here to ignore PRs which only change documentation. This can cause a problem, see the workflow file +# "validate.skip.yml" for a description of the problem and the solution provided in that file. +on: + push: + paths-ignore: + - 'doc/**' + - '**/README.md' + - 'CONTRIBUTING.md' + branches: + - master + pull_request: + paths-ignore: + - 'doc/**' + - '**/README.md' + - 'CONTRIBUTING.md' + release: + types: + - created + +env: + # We choose a stable ghc version across all os's + # which will be used to do the next release + GHC_FOR_RELEASE: '9.2.8' + # Ideally we should use the version about to be released for hackage tests and benchmarks + GHC_FOR_SOLVER_BENCHMARKS: '9.2.8' + GHC_FOR_COMPLETE_HACKAGE_TESTS: '9.2.8' + COMMON_FLAGS: '-j 2 -v' + +jobs: + validate: + name: Validate ${{ matrix.os }} ghc-${{ matrix.ghc }} + runs-on: ${{ matrix.os }} + outputs: + GHC_FOR_RELEASE: ${{ format('["{0}"]', env.GHC_FOR_RELEASE) }} + strategy: + matrix: + os: ["ubuntu-latest", "macos-latest", "windows-latest"] + ghc: ["9.6.3", "9.4.7", "9.2.8", "9.0.2", "8.10.7", "8.8.4", "8.6.5", "8.4.4"] + exclude: + # corrupts GHA cache or the fabric of reality itself, see https://github.com/haskell/cabal/issues/8356 + - os: "windows-latest" + ghc: "8.10.7" + # lot of segfaults caused by ghc bugs + - os: "windows-latest" + ghc: "8.8.4" + # it also throws segfaults randomly + - os: "windows-latest" + ghc: "8.4.4" + # it often randomly does "C:\Users\RUNNER~1\AppData\Local\Temp\ghcFEDE.c: DeleteFile "\\\\?\\C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\ghcFEDE.c": permission denied (Access is denied.)" + - os: "windows-latest" + ghc: "8.6.5" + + steps: + + - uses: actions/checkout@v4 + + # See the following link for a breakdown of the following step + # https://github.com/haskell/actions/issues/7#issuecomment-745697160 + - uses: actions/cache@v3 + with: + # validate.sh uses a special build dir + path: | + ${{ steps.setup-haskell.outputs.cabal-store }} + dist-* + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-${{ matrix.ghc }}- + + - uses: haskell/actions/setup@v2 + id: setup-haskell + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: '3.10.1.0' + + - name: Work around git problem https://bugs.launchpad.net/ubuntu/+source/git/+bug/1993586 (cabal PR #8546) + run: | + git config --global protocol.file.allow always + + # The tool is not essential to the rest of the test suite. If + # hackage-repo-tool is not present, any test that requires it will + # be skipped. + # We want to keep this in the loop but we don't want to fail if + # hackage-repo-tool breaks or fails to support a newer GHC version. + - name: Install hackage-repo-tool + continue-on-error: true + run: | + cd $(mktemp -d) + cabal install hackage-repo-tool + + # Needed by cabal-testsuite/PackageTests/Configure/setup.test.hs + - name: Install Autotools + if: runner.os == 'macOS' + run: | + brew install automake + + - name: Set validate inputs + run: | + FLAGS="${{ env.COMMON_FLAGS }}" + if [[ ${{ matrix.ghc }} == ${{ env.GHC_FOR_SOLVER_BENCHMARKS }} ]]; then + FLAGS="$FLAGS --solver-benchmarks" + fi + if [[ ${{ matrix.ghc }} == ${{ env.GHC_FOR_COMPLETE_HACKAGE_TESTS }} ]]; then + FLAGS="$FLAGS --complete-hackage-tests" + fi + echo "FLAGS=$FLAGS" >> $GITHUB_ENV + + - name: Allow newer dependencies when built with latest GHC + if: ${{ matrix.ghc }} == '9.6.3' + run: | + echo "allow-newer: rere:base, rere:transformers" >> cabal.project.validate + + - name: Validate print-config + run: sh validate.sh $FLAGS -s print-config + + - name: Validate print-tool-versions + run: sh validate.sh $FLAGS -s print-tool-versions + + - name: Validate build + run: sh validate.sh $FLAGS -s build + + - name: Tar cabal head executable + if: matrix.ghc == env.GHC_FOR_RELEASE + run: | + CABAL_EXEC=$(cabal list-bin --builddir=dist-newstyle-validate-ghc-${{ matrix.ghc }} --project-file=cabal.project.validate cabal-install:exe:cabal) + # We have to tar the executable to preserve executable permissions + # see https://github.com/actions/upload-artifact/issues/38 + if [[ ${{ runner.os }} == 'Windows' ]]; then + # `cabal list-bin` gives us a windows path but tar needs the posix one + CABAL_EXEC=$(cygpath $CABAL_EXEC) + fi + if [[ "${{ runner.os }}" == "macOS" ]]; then + # Workaround to avoid bsdtar corrupts the executable + # so executing it after untar throws `cannot execute binary file` + # see https://github.com/actions/virtual-environments/issues/2619#issuecomment-788397841 + sudo /usr/sbin/purge + fi + export CABAL_EXEC_TAR="cabal-head-${{ runner.os }}-x86_64.tar.gz" + tar -czvf $CABAL_EXEC_TAR -C $(dirname "$CABAL_EXEC") $(basename "$CABAL_EXEC") + echo "CABAL_EXEC_TAR=$CABAL_EXEC_TAR" >> $GITHUB_ENV + + # We upload the cabal executable built with the ghc used in the release for: + # - Reuse it in the dogfooding job (although we could use the cached build dir) + # - Make it available in the workflow to make easier testing it locally + - name: Upload cabal-install executable to workflow artifacts + if: matrix.ghc == env.GHC_FOR_RELEASE + uses: actions/upload-artifact@v3 + with: + name: cabal-${{ runner.os }}-x86_64 + path: ${{ env.CABAL_EXEC_TAR }} + + - name: Validate lib-tests + env: + # `rawSystemStdInOut reports text decoding errors` + # test does not find ghc without the full path in windows + GHCPATH: ${{ steps.setup-haskell.outputs.ghc-exe }} + run: sh validate.sh $FLAGS -s lib-tests + + - name: Validate lib-suite + # Have to disable *-suite validation: + # - the Windows@9.6.1 problem is tracked at https://github.com/haskell/cabal/issues/8858 + # - but curently can't run it with GHC 9.6, tracking: https://github.com/haskell/cabal/issues/8883 + run: sh validate.sh $FLAGS -s lib-suite + + - name: Validate cli-tests + run: sh validate.sh $FLAGS -s cli-tests + + - name: Validate cli-suite + # Have to disable *-suite validation, see above the comment for lib-suite + run: sh validate.sh $FLAGS -s cli-suite + + validate-old-ghcs: + name: Validate old ghcs ${{ matrix.extra-ghc }} + runs-on: ubuntu-latest + needs: validate + # This job needs an older ubuntu (16.04) cause + # the required old ghcs using the `-dyn` flavour + # are not installable from ppa/hvr in newer ones + # see https://github.com/haskell/cabal/issues/8011 + container: + image: phadej/ghc:8.8.4-xenial + + strategy: + matrix: + # Newer ghc versions than 8.8.4 have to be installed with ghcup cause + # they are not available in ppa/hvr. The ghcup installation + # needs `sudo` which is not available in the xenial container + ghc: ["8.8.4"] + extra-ghc: ["7.10.3", "7.8.4", "7.6.3", "7.4.2", "7.2.2", "7.0.4"] + + steps: + + # We can't use actions/checkout with the xenial docker container + # cause it does not work with the git version included in it, see: + # https://github.com/actions/checkout/issues/170 + # https://github.com/actions/checkout/issues/295 + # - uses: actions/checkout@v4 + - name: Checkout + run: | + echo $GITHUB_REF $GITHUB_SHA + git clone --depth 1 https://github.com/$GITHUB_REPOSITORY.git . + git fetch origin $GITHUB_SHA:temporary-ci-branch + git checkout $GITHUB_SHA || (git fetch && git checkout $GITHUB_SHA) + + - name: Install extra compiler + run: | + apt-get update + apt-get install -y ghc-${{ matrix.extra-ghc }}-dyn + + - uses: haskell/actions/setup@v2 + id: setup-haskell + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: latest # latest is mandatory for cabal-testsuite, see https://github.com/haskell/cabal/issues/8133 + + # As we are reusing the cached build dir from the previous step + # the generated artifacts are available here, + # including the cabal executable and the test suite + - uses: actions/cache@v3 + with: + path: | + ${{ steps.setup-haskell.outputs.cabal-store }} + dist-* + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-${{ matrix.ghc }}- + + - name: Validate build + run: sh validate.sh ${{ env.COMMON_FLAGS }} -s build + + - name: "Validate lib-suite-extras --extra-hc ghc-${{ matrix.extra-ghc }}" + env: + EXTRA_GHC: "/opt/ghc/${{ matrix.extra-ghc }}/bin/ghc-${{ matrix.extra-ghc }}" + run: sh validate.sh ${{ env.COMMON_FLAGS }} --lib-only -s lib-suite-extras --extra-hc ${{ env.EXTRA_GHC }} + + # The previous jobs use a released version of cabal to build cabal HEAD itself + # This one uses the cabal HEAD generated executable in the previous step + # to build itself again, as sanity check + dogfooding: + name: Dogfooding ${{ matrix.os }} ghc-${{ matrix.ghc }} + runs-on: ${{ matrix.os }} + needs: validate + strategy: + matrix: + os: ["ubuntu-latest", "macos-latest", "windows-latest"] + # We only use one ghc version the used one for the next release (defined at top of the workflow) + # We need to build an array dynamically to inject the appropiate env var in a previous job, + # see https://docs.github.com/en/actions/learn-github-actions/expressions#fromjson + ghc: ${{ fromJSON (needs.validate.outputs.GHC_FOR_RELEASE) }} + + steps: + - uses: actions/checkout@v4 + + # See https://github.com/haskell/cabal/pull/8739 + - name: Sudo chmod to permit ghcup to update its cache + run: | + if [[ "${{ runner.os }}" == "Linux" ]]; then + sudo ls -lah /usr/local/.ghcup/cache + sudo mkdir -p /usr/local/.ghcup/cache + sudo ls -lah /usr/local/.ghcup/cache + sudo chown -R $USER /usr/local/.ghcup + sudo chmod -R 777 /usr/local/.ghcup + fi + - uses: haskell/actions/setup@v2 + id: setup-haskell + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: latest # default, we are not using it in this job + + - name: Download cabal executable from workflow artifacts + uses: actions/download-artifact@v3 + with: + name: cabal-${{ runner.os }}-x86_64 + path: cabal-head + + - name: Untar the cabal executable + run: tar -xzf ./cabal-head/cabal-head-${{ runner.os }}-x86_64.tar.gz -C cabal-head + + - name: print-config using cabal HEAD + run: sh validate.sh ${{ env.COMMON_FLAGS }} --with-cabal ./cabal-head/cabal -s print-config + + # We dont use cache to force a build with a fresh store dir and build dir + # This way we check cabal can build all its dependencies + - name: Build using cabal HEAD + run: sh validate.sh ${{ env.COMMON_FLAGS }} --with-cabal ./cabal-head/cabal -s build + + prerelease-head: + name: Create a GitHub prerelease with the binary artifacts + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/master' + + # IMPORTANT! Any job added to the workflow should be added here too + needs: [validate, validate-old-ghcs, dogfooding] + + steps: + - uses: actions/download-artifact@v3 + with: + name: cabal-Windows-x86_64 + + - uses: actions/download-artifact@v3 + with: + name: cabal-Linux-x86_64 + + - uses: actions/download-artifact@v3 + with: + name: cabal-macOS-x86_64 + + - name: Create GitHub prerelease + uses: "marvinpinto/action-automatic-releases@v1.2.1" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "cabal-head" + prerelease: true + title: "cabal-head" + files: | + cabal-head-Windows-x86_64.tar.gz + cabal-head-Linux-x86_64.tar.gz + cabal-head-macOS-x86_64.tar.gz + + # We use this job as a summary of the workflow + # It will fail if any of the previous jobs does it + # This way we can use it exclusively in branch protection rules + # and abstract away the concrete jobs of the workflow, including their names + validate-post-job: + if: always() + name: Validate post job + runs-on: ubuntu-latest + # IMPORTANT! Any job added to the workflow should be added here too + needs: [validate, validate-old-ghcs, dogfooding] + + steps: + - run: | + echo "jobs info: ${{ toJSON(needs) }}" + - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + run: exit 1 diff --git a/.gitlab/brew.sh b/.gitlab/brew.sh new file mode 100644 index 00000000000..b2771811d47 --- /dev/null +++ b/.gitlab/brew.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Install brew locally in the project dir. Packages will also be installed here. +# FIXME: Use brew in supported way. See +# https://docs.brew.sh/Installation#untar-anywhere-unsupported +brew_dir="${CI_PROJECT_DIR}/.brew" + +if [ ! -e "${brew_dir}" ]; then + mkdir -p "${brew_dir}" + curl --fail -L "https://github.com/Homebrew/brew/archive/refs/tags/${BREW_VERSION}.tar.gz" | tar xz --strip 1 -C "${brew_dir}" +fi + +export PATH="${brew_dir}/bin:${brew_dir}/sbin:$PATH" + +# make sure to not pollute the machine with temp files etc +mkdir -p $CI_PROJECT_DIR/.brew_cache +export HOMEBREW_CACHE=$CI_PROJECT_DIR/.brew_cache +mkdir -p $CI_PROJECT_DIR/.brew_logs +export HOMEBREW_LOGS=$CI_PROJECT_DIR/.brew_logs +mkdir -p /private/tmp/.brew_tmp +export HOMEBREW_TEMP=/private/tmp/.brew_tmp + +# update and install packages +brew update +brew install ${1+"$@"} diff --git a/.gitlab/ci.sh b/.gitlab/ci.sh index 19a9f9c4cdf..c856f9f2cb9 100755 --- a/.gitlab/ci.sh +++ b/.gitlab/ci.sh @@ -4,18 +4,19 @@ set -Eeuo pipefail source "$CI_PROJECT_DIR/.gitlab/common.sh" - export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR/toolchain" export CABAL_DIR="$CI_PROJECT_DIR/cabal" case "$(uname)" in MSYS_*|MINGW*) export CABAL_DIR="$(cygpath -w "$CABAL_DIR")" - GHCUP_BINDIR="${GHCUP_INSTALL_BASE_PREFIX}/ghcup/bin" + GHCUP_BINDIR="${GHCUP_INSTALL_BASE_PREFIX}/ghcup/bin" + EXE_EXT=".exe" + ;; + *) + GHCUP_BINDIR="${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin" + EXE_EXT="" ;; - *) - GHCUP_BINDIR="${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin" - ;; esac mkdir -p "$CABAL_DIR" @@ -25,13 +26,34 @@ export PATH="$GHCUP_BINDIR:$PATH" export BOOTSTRAP_HASKELL_NONINTERACTIVE=1 export BOOTSTRAP_HASKELL_GHC_VERSION=$GHC_VERSION export BOOTSTRAP_HASKELL_CABAL_VERSION=$CABAL_INSTALL_VERSION -export BOOTSTRAP_HASKELL_VERBOSE=1 export BOOTSTRAP_HASKELL_ADJUST_CABAL_CONFIG=yes +# We don't use stack, and it isn't available on i386-deb9 +export BOOTSTRAP_HASKELL_INSTALL_NO_STACK=yes -curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh +# for some reason the subshell doesn't pick up the arm64 environment on darwin +# and starts installing x86_64 GHC +case "$(uname -s)" in + "Darwin"|"darwin") + case "$(/usr/bin/arch)" in + aarch64|arm64|armv8l) + curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | arch -arm64 /bin/bash + ;; + *) + curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh + ;; + esac + ;; + *) + curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh + ;; +esac # https://github.com/haskell/cabal/issues/7313#issuecomment-811851884 -if [ "$(getconf LONG_BIT)" == "32" ] ; then +# and +# https://github.com/haskellari/lukko/issues/17 +# +# $PLATFORM comes from CI. +if [ "$(getconf LONG_BIT)" = "32" -o "${PLATFORM:=xxx}" = "x86_64-linux-centos7" ] ; then echo 'constraints: lukko -ofd-locking' >> cabal.project.release.local fi @@ -43,10 +65,11 @@ args=( ${ADD_CABAL_ARGS} ) +run cabal update hackage.haskell.org,HEAD run cabal v2-build ${args[@]} cabal-install mkdir "$CI_PROJECT_DIR/out" -cp "$(cabal list-bin ${args[@]} cabal-install:exe:cabal)" "$CI_PROJECT_DIR/out/cabal" +cp "$(cabal list-bin ${args[@]} cabal-install:exe:cabal)" "$CI_PROJECT_DIR/out/cabal$EXE_EXT" cp dist-newstyle/cache/plan.json "$CI_PROJECT_DIR/out/plan.json" cd "$CI_PROJECT_DIR/out/" @@ -54,10 +77,10 @@ cd "$CI_PROJECT_DIR/out/" TARBALL_PREFIX="cabal-install-$("$CI_PROJECT_DIR/out/cabal" --numeric-version)" case "${TARBALL_EXT}" in zip) - zip "${TARBALL_PREFIX}-${TARBALL_ARCHIVE_SUFFIX}.${TARBALL_EXT}" cabal plan.json + zip "${TARBALL_PREFIX}-${TARBALL_ARCHIVE_SUFFIX}.${TARBALL_EXT}" "cabal${EXE_EXT}" plan.json ;; tar.xz) - tar caf "${TARBALL_PREFIX}-${TARBALL_ARCHIVE_SUFFIX}.${TARBALL_EXT}" cabal plan.json + tar caf "${TARBALL_PREFIX}-${TARBALL_ARCHIVE_SUFFIX}.${TARBALL_EXT}" "cabal${EXE_EXT}" plan.json ;; *) fail "Unknown TARBALL_EXT: ${TARBALL_EXT}"