diff --git a/docs/src/user.md b/docs/src/user.md index c213422..d2771ac 100644 --- a/docs/src/user.md +++ b/docs/src/user.md @@ -50,6 +50,7 @@ These plugins will create the configuration files of common CI services for you. AppVeyor CirrusCI DroneCI +GitHubActions GitLabCI TravisCI ``` @@ -91,9 +92,9 @@ Template(; plugins=[ License(; name="MPL"), Git(; manifest=true, ssh=true), - TravisCI(; x86=true), + GitHubActions(; x86=true), Codecov(), - Documenter{TravisCI}(), + Documenter{GitHubActions}(), Develop(), ], ) diff --git a/src/PkgTemplates.jl b/src/PkgTemplates.jl index 5221245..2e080db 100644 --- a/src/PkgTemplates.jl +++ b/src/PkgTemplates.jl @@ -22,6 +22,7 @@ export Develop, Documenter, Git, + GitHubActions, GitLabCI, License, ProjectFile, diff --git a/src/plugins/ci.jl b/src/plugins/ci.jl index 7db08da..de18b96 100644 --- a/src/plugins/ci.jl +++ b/src/plugins/ci.jl @@ -13,14 +13,84 @@ const DEFAULT_CI_VERSIONS_NO_NIGHTLY = map(format_version, [default_version(), V const EXTRA_VERSIONS_DOC = "- `extra_versions::Vector`: Extra Julia versions to test, as strings or `VersionNumber`s." """ - collect_versions(t::Template, versions::Vector) -> Vector{String} + GitHubActions(; + file="$(contractuser(default_file("github", "workflows", "ci.yml")))", + destination="ci.yml", + linux=true, + osx=true, + windows=true, + x64=true, + x86=false, + coverage=true, + extra_versions=$DEFAULT_CI_VERSIONS_NO_NIGHTLY, + ) -Combine `t`'s Julia version with `versions`, and format them as `major.minor`. -This is useful for creating lists of versions to be included in CI configurations. +Integrates your packages with [GitHub Actions](https://github.com/features/actions). + +## Keyword Arguments +- `file::AbstractString`: Template file for the workflow file. +- `destination::AbstractString`: Destination of the worflow file, + relative to `.github/workflows`. +- `linux::Bool`: Whether or not to run builds on Linux. +- `osx::Bool`: Whether or not to run builds on OSX (MacOS). +- `windows::Bool`: Whether or not to run builds on Windows. +- `x64::Bool`: Whether or not to run builds on 64-bit architecture. +- `x86::Bool`: Whether or not to run builds on 32-bit architecture. +- `coverage::Bool`: Whether or not to publish code coverage. + Another code coverage plugin such as [`Codecov`](@ref) must also be included. +$EXTRA_VERSIONS_DOC + +!!! note + If using coverage plugins, don't forget to manually add your API tokens as secrets, + as described [here](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets). + +!!! note + Nightly Julia is not supported. """ -function collect_versions(t::Template, versions::Vector) - vs = map(format_version, [t.julia, versions...]) - return sort(unique(vs)) +@with_kw_noshow struct GitHubActions <: BasicPlugin + file::String = default_file("github", "workflows", "ci.yml") + destination::String = "ci.yml" + linux::Bool = true + osx::Bool = true + windows::Bool = true + x64::Bool = true + x86::Bool = false + coverage::Bool = true + extra_versions::Vector = DEFAULT_CI_VERSIONS_NO_NIGHTLY +end + +source(p::GitHubActions) = p.file +destination(p::GitHubActions) = joinpath(".github", "workflows", p.destination) + +tags(::GitHubActions) = "<<", ">>" + +badges(p::GitHubActions) = Badge( + "Build Status", + "https://github.com/{{{USER}}}/{{{PKG}}}.jl/actions", + "https://github.com/{{{USER}}}/{{{PKG}}}.jl/workflows/CI/badge.svg", +) + +function view(p::GitHubActions, t::Template, pkg::AbstractString) + os = String[] + p.linux && push!(os, "ubuntu-latest") + p.osx && push!(os, "macOS-latest") + p.windows && push!(os, "windows-latest") + arch = filter(a -> getfield(p, Symbol(a)), ["x64", "x86"]) + excludes = Dict{String, String}[] + p.osx && p.x86 && push!(excludes, Dict("E_OS" => "macOS-latest", "E_ARCH" => "x86")) + + return Dict( + "ARCH" => arch, + "EXCLUDES" => excludes, + "HAS_CODECOV" => p.coverage && hasplugin(t, Codecov), + "HAS_COVERALLS" => p.coverage && hasplugin(t, Coveralls), + "HAS_DOCUMENTER" => hasplugin(t, Documenter{GitHubActions}), + "HAS_EXCLUDES" => !isempty(excludes), + "OS" => os, + "PKG" => pkg, + "USER" => t.user, + "VERSIONS" => collect_versions(t, p.extra_versions), + ) end """ @@ -319,6 +389,17 @@ function view(p::DroneCI, t::Template, pkg::AbstractString) ) end +""" + collect_versions(t::Template, versions::Vector) -> Vector{String} + +Combine `t`'s Julia version with `versions`, and format them as `major.minor`. +This is useful for creating lists of versions to be included in CI configurations. +""" +function collect_versions(t::Template, versions::Vector) + vs = map(format_version, [t.julia, versions...]) + return sort(unique(vs)) +end + """ is_ci(::Plugin) -> Bool @@ -326,6 +407,6 @@ Determine whether or not a plugin is a CI plugin. If you are adding a CI plugin, you should implement this function and return `true`. """ is_ci(::Plugin) = false -is_ci(::Union{AppVeyor, TravisCI, CirrusCI, GitLabCI, DroneCI}) = true +is_ci(::Union{AppVeyor, GitHubActions, TravisCI, CirrusCI, GitLabCI, DroneCI}) = true -needs_username(::Union{AppVeyor, TravisCI, CirrusCI, GitLabCI, DroneCI}) = true +needs_username(::Union{AppVeyor, GitHubActions, TravisCI, CirrusCI, GitLabCI, DroneCI}) = true diff --git a/src/plugins/documenter.jl b/src/plugins/documenter.jl index 87541b3..227e39d 100644 --- a/src/plugins/documenter.jl +++ b/src/plugins/documenter.jl @@ -3,6 +3,9 @@ const DOCUMENTER_DEP = PackageSpec(; uuid="e30172f5-a6a5-5a46-863b-614d45cd2de4", ) +const DeployStyle = Union{TravisCI, GitHubActions, GitLabCI, Nothing} +const GitHubPagesStyle = Union{TravisCI, GitHubActions} + """ Documenter{T<:Union{TravisCI, GitLabCI, Nothing}}(; make_jl="$(contractuser(default_file("docs", "make.jl")))", @@ -17,6 +20,8 @@ Documentation deployment depends on `T`, where `T` is some supported CI plugin, or `Nothing` to only support local documentation builds. ## Supported Type Parameters +- `GitHubActions`: Deploys documentation to [GitHub Pages](https://pages.github.com) + with the help of [`GitHubActions`](@ref). - `TravisCI`: Deploys documentation to [GitHub Pages](https://pages.github.com) with the help of [`TravisCI`](@ref). - `GitLabCI`: Deploys documentation to [GitLab Pages](https://pages.gitlab.com) @@ -37,7 +42,7 @@ or `Nothing` to only support local documentation builds. If deploying documentation with Travis CI, don't forget to complete [the required configuration](https://juliadocs.github.io/Documenter.jl/stable/man/hosting/#SSH-Deploy-Keys-1). """ -struct Documenter{T<:Union{TravisCI, GitLabCI, Nothing}} <: Plugin +struct Documenter{T<:DeployStyle} <: Plugin assets::Vector{String} makedocs_kwargs::Dict{Symbol} canonical_url::Union{Function, Nothing} @@ -51,7 +56,7 @@ struct Documenter{T<:Union{TravisCI, GitLabCI, Nothing}} <: Plugin canonical_url::Union{Function, Nothing}=make_canonical(T), make_jl::AbstractString=default_file("docs", "make.jl"), index_md::AbstractString=default_file("docs", "src", "index.md"), - ) where T <: Union{TravisCI, GitLabCI, Nothing} + ) where T <: DeployStyle return new(assets, makedocs_kwargs, canonical_url, make_jl, index_md) end end @@ -61,7 +66,7 @@ Documenter(; kwargs...) = Documenter{Nothing}(; kwargs...) gitignore(::Documenter) = ["/docs/build/"] badges(::Documenter) = Badge[] -badges(::Documenter{TravisCI}) = [ +badges(::Documenter{<:GitHubPagesStyle}) = [ Badge( "Stable", "https://img.shields.io/badge/docs-stable-blue.svg", @@ -90,18 +95,17 @@ view(p::Documenter, t::Template, pkg::AbstractString) = Dict( "USER" => t.user, ) -function view(p::Documenter{TravisCI}, t::Template, pkg::AbstractString) +function view(p::Documenter{<:GitHubPagesStyle}, t::Template, pkg::AbstractString) base = invoke(view, Tuple{Documenter, Template, AbstractString}, p, t, pkg) return merge(base, Dict("HAS_DEPLOY" => true)) end -foreach((TravisCI, GitLabCI)) do T - @eval function validate(::Documenter{$T}, t::Template) - if !hasplugin(t, $T) - name = nameof($T) - s = "Documenter: The $name plugin must be included for docs deployment to be set up" - throw(ArgumentError(s)) - end +validate(::Documenter{Nothing}, ::Template) = nothing +function validate(::Documenter{T}, t::Template) where T <: DeployStyle + if !hasplugin(t, T) + name = nameof(T) + s = "Documenter: The $name plugin must be included for docs deployment to be set up" + throw(ArgumentError(s)) end end @@ -127,7 +131,7 @@ end github_pages_url(t::Template, pkg::AbstractString) = "https://$(t.user).github.io/$pkg.jl" gitlab_pages_url(t::Template, pkg::AbstractString) = "https://$(t.user).gitlab.io/$pkg.jl" -make_canonical(::Type{TravisCI}) = github_pages_url +make_canonical(::Type{<:GitHubPagesStyle}) = github_pages_url make_canonical(::Type{GitLabCI}) = gitlab_pages_url make_canonical(::Type{Nothing}) = nothing diff --git a/src/plugins/readme.jl b/src/plugins/readme.jl index 8f80dc0..f519661 100644 --- a/src/plugins/readme.jl +++ b/src/plugins/readme.jl @@ -48,6 +48,7 @@ end badge_order() = [ Documenter{GitLabCI}, Documenter{TravisCI}, + GitHubActions, GitLabCI, TravisCI, AppVeyor, diff --git a/templates/github/workflows/ci.yml b/templates/github/workflows/ci.yml new file mode 100644 index 0000000..d02a57a --- /dev/null +++ b/templates/github/workflows/ci.yml @@ -0,0 +1,67 @@ +name: CI +on: + - push + - pull_request +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + version: + <<#VERSIONS>> + - '<<&.>>' + <> + os: + <<#OS>> + - <<&.>> + <> + arch: + <<#ARCH>> + - <<&.>> + <> + <<#HAS_EXCLUDES>> + exclude: + <> + <<#EXCLUDES>> + - os: <<&E_OS>> + arch: <<&E_ARCH>> + <<#E_VERSION>> + version: '<<&E_VERSION>>' + <> + <> + steps: + - uses: actions/checkout@v1 + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/julia-buildpkg@latest + - uses: julia-actions/julia-runtest@latest + <<#HAS_CODECOV>> + - uses: julia-actions/julia-uploadcodecov@latest + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + <> + <<#HAS_COVERALLS>> + - uses: julia-actions/julia-uploadcoveralls@latest + env: + COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} + <> + <<#HAS_DOCUMENTER>> + docs: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: julia-actions/setup-julia@latest + with: + version: '1.0' + - run: julia --project=docs -e ' + using Pkg; + Pkg.develop(PackageSpec(; path=pwd())); + Pkg.instantiate();' + - run: julia --project=docs docs/make.jl + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + <> diff --git a/test/fixtures/AllPlugins/.github/workflows/ci.yml b/test/fixtures/AllPlugins/.github/workflows/ci.yml new file mode 100644 index 0000000..2710c61 --- /dev/null +++ b/test/fixtures/AllPlugins/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI +on: + - push + - pull_request +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + version: + - '1.0' + - '1.2' + os: + - ubuntu-latest + - macOS-latest + - windows-latest + arch: + - x64 + steps: + - uses: actions/checkout@v1 + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/julia-buildpkg@latest + - uses: julia-actions/julia-runtest@latest + - uses: julia-actions/julia-uploadcodecov@latest + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + - uses: julia-actions/julia-uploadcoveralls@latest + env: + COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} diff --git a/test/fixtures/AllPlugins/README.md b/test/fixtures/AllPlugins/README.md index c641696..b2b41aa 100644 --- a/test/fixtures/AllPlugins/README.md +++ b/test/fixtures/AllPlugins/README.md @@ -1,5 +1,6 @@ # AllPlugins +[![Build Status](https://github.com/tester/AllPlugins.jl/actions)](https://github.com/tester/AllPlugins.jl/workflows/CI/badge.svg) [![Build Status](https://gitlab.com/tester/AllPlugins.jl/badges/master/build.svg)](https://gitlab.com/tester/AllPlugins.jl/pipelines) [![Coverage](https://gitlab.com/tester/AllPlugins.jl/badges/master/coverage.svg)](https://gitlab.com/tester/AllPlugins.jl/commits/master) [![Build Status](https://travis-ci.com/tester/AllPlugins.jl.svg?branch=master)](https://travis-ci.com/tester/AllPlugins.jl) diff --git a/test/fixtures/WackyOptions/.github/workflows/ci.yml b/test/fixtures/WackyOptions/.github/workflows/ci.yml new file mode 100644 index 0000000..ff776ce --- /dev/null +++ b/test/fixtures/WackyOptions/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI +on: + - push + - pull_request +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + version: + - '1.0' + - '1.2' + os: + - macOS-latest + - windows-latest + arch: + - x64 + - x86 + exclude: + - os: macOS-latest + arch: x86 + steps: + - uses: actions/checkout@v1 + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/julia-buildpkg@latest + - uses: julia-actions/julia-runtest@latest diff --git a/test/fixtures/WackyOptions/README.md b/test/fixtures/WackyOptions/README.md index d7c163b..9a9d4aa 100644 --- a/test/fixtures/WackyOptions/README.md +++ b/test/fixtures/WackyOptions/README.md @@ -1,4 +1,4 @@ -# WackyOptions [![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://tester.gitlab.io/WackyOptions.jl/dev) [![Build Status](https://gitlab.com/tester/WackyOptions.jl/badges/master/build.svg)](https://gitlab.com/tester/WackyOptions.jl/pipelines) [![Build Status](https://travis-ci.com/tester/WackyOptions.jl.svg?branch=master)](https://travis-ci.com/tester/WackyOptions.jl) [![Build Status](https://ci.appveyor.com/api/projects/status/github/tester/WackyOptions.jl?svg=true)](https://ci.appveyor.com/project/tester/WackyOptions-jl) [![Build Status](https://cloud.drone.io/api/badges/tester/WackyOptions.jl/status.svg)](https://cloud.drone.io/tester/WackyOptions.jl) [![Build Status](https://api.cirrus-ci.com/github/tester/WackyOptions.jl.svg)](https://cirrus-ci.com/github/tester/WackyOptions.jl) [![Coverage](https://codecov.io/gh//.jl/branch/master/graph/badge.svg)](https://codecov.io/gh//.jl) [![Coverage](https://coveralls.io/repos/github//.jl/badge.svg?branch=master)](https://coveralls.io/github//.jl?branch=master) +# WackyOptions [![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://tester.gitlab.io/WackyOptions.jl/dev) [![Build Status](https://github.com/tester/WackyOptions.jl/actions)](https://github.com/tester/WackyOptions.jl/workflows/CI/badge.svg) [![Build Status](https://gitlab.com/tester/WackyOptions.jl/badges/master/build.svg)](https://gitlab.com/tester/WackyOptions.jl/pipelines) [![Build Status](https://travis-ci.com/tester/WackyOptions.jl.svg?branch=master)](https://travis-ci.com/tester/WackyOptions.jl) [![Build Status](https://ci.appveyor.com/api/projects/status/github/tester/WackyOptions.jl?svg=true)](https://ci.appveyor.com/project/tester/WackyOptions-jl) [![Build Status](https://cloud.drone.io/api/badges/tester/WackyOptions.jl/status.svg)](https://cloud.drone.io/tester/WackyOptions.jl) [![Build Status](https://api.cirrus-ci.com/github/tester/WackyOptions.jl.svg)](https://cirrus-ci.com/github/tester/WackyOptions.jl) [![Coverage](https://codecov.io/gh//.jl/branch/master/graph/badge.svg)](https://codecov.io/gh//.jl) [![Coverage](https://coveralls.io/repos/github//.jl/badge.svg?branch=master)](https://coveralls.io/github//.jl?branch=master) ## Citing diff --git a/test/fixtures/WackyOptions/docs/Manifest.toml b/test/fixtures/WackyOptions/docs/Manifest.toml index 2fbd4f4..286113b 100644 --- a/test/fixtures/WackyOptions/docs/Manifest.toml +++ b/test/fixtures/WackyOptions/docs/Manifest.toml @@ -48,9 +48,9 @@ uuid = "a63ad114-7e13-5084-954f-fe012c677804" [[Parsers]] deps = ["Dates", "Test"] -git-tree-sha1 = "ef0af6c8601db18c282d092ccbd2f01f3f0cd70b" +git-tree-sha1 = "c56ecb484f286639f161e712b8311f5ab77e8d32" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "0.3.7" +version = "0.3.8" [[Pkg]] deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] diff --git a/test/reference.jl b/test/reference.jl index 55c0b69..b0adb93 100644 --- a/test/reference.jl +++ b/test/reference.jl @@ -22,8 +22,8 @@ end @testset "All plugins" begin test_all("AllPlugins"; authors=USER, plugins=[ - AppVeyor(), CirrusCI(), Citation(), Codecov(), Coveralls(), - Develop(), Documenter(), DroneCI(), GitLabCI(), TravisCI(), + AppVeyor(), CirrusCI(), Citation(), Codecov(), Coveralls(), Develop(), + Documenter(), DroneCI(), GitHubActions(), GitLabCI(), TravisCI(), ]) end @@ -41,6 +41,7 @@ end ), DroneCI(; amd64=false, arm=true, arm64=true, extra_versions=["1.1"]), Git(; ignore=["a", "b", "c"], manifest=true), + GitHubActions(; x86=true, linux=false, coverage=false), GitLabCI(; coverage=false, extra_versions=[v"0.6"]), License(; name="ISC"), ProjectFile(; version=v"1"), diff --git a/test/template.jl b/test/template.jl index bea4705..aaa1f44 100644 --- a/test/template.jl +++ b/test/template.jl @@ -61,6 +61,9 @@ end @testset "validate" begin + foreach((GitHubActions, TravisCI, GitLabCI)) do T + @test_throws ArgumentError tpl(; plugins=[Documenter{T}()]) + end mock(LibGit2.getconfig => (_k, _d) -> "") do _gc @test_throws ArgumentError tpl(; plugins=[Git()]) end