Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bot for Github actions #49

Merged
merged 40 commits into from
Jan 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ba52820
precompileActivator and precompileDeactivator
aminya Dec 6, 2019
281b1e3
- Refractiring precompilePath - Input args changed - removed extra me…
aminya Dec 12, 2019
83b052c
Support for packageName being Symbol or Module
aminya Dec 12, 2019
e90bd64
Add tests
aminya Dec 12, 2019
8a76cea
Merge branch 'master' into packageSnooper
aminya Dec 12, 2019
af397ab
include("utilities.jl")
aminya Dec 12, 2019
b1db5b6
snoopiBot
aminya Dec 12, 2019
b157750
Base.read
aminya Dec 12, 2019
0e3084c
\" instead of "
aminya Dec 12, 2019
ef60d60
Base.write
aminya Dec 12, 2019
1563269
QuoteNode(snoopScript)
aminya Dec 12, 2019
a0fa8fd
package symbol
aminya Dec 12, 2019
2bec05c
default runtests for @snoopiBot
aminya Dec 12, 2019
a6e13dd
move packageSym inside
aminya Dec 12, 2019
cf79ab3
Fixed bug of calling inner macro
aminya Dec 12, 2019
657ae4c
Fix tests
aminya Dec 12, 2019
a238b73
using Pkg.test for default method
aminya Dec 12, 2019
52b1f5e
(de)activation progress print
aminya Dec 28, 2019
50e1069
Using regex to detect space between # and include
aminya Dec 28, 2019
143170b
Fix path bug
aminya Dec 28, 2019
a602c93
[skip ci] safe local developement
aminya Dec 28, 2019
4fe0bf3
snoopiBot for runtests
aminya Dec 28, 2019
f81f64b
Tests for activator and deactivator
aminya Dec 29, 2019
6995719
Add doc for SnoopCompile bot
aminya Dec 29, 2019
450794f
Rename utilities to bot
aminya Dec 29, 2019
a42a8d2
Add test dependencies
aminya Dec 29, 2019
c820a55
Moving timesum to bot.jl, Changing docstring
aminya Dec 30, 2019
dc6236e
Examples for snoopiBot
aminya Dec 30, 2019
bf9b0f7
snoopiBenchBot
aminya Dec 30, 2019
8c4ed68
Adding doc for benchmarking
aminya Dec 30, 2019
44a2d18
Adding print messages to benchmark
aminya Dec 30, 2019
ee71342
Adding tests
aminya Dec 30, 2019
ac23d88
import Pkg
aminya Dec 30, 2019
b066fe6
Adding blacklist to snoopiBot
aminya Dec 30, 2019
c27fc05
Adding BotConfig object
aminya Dec 31, 2019
cdea651
Code and file organization
aminya Jan 1, 2020
52b2da8
Adding subst to BotConfig
aminya Jan 1, 2020
2ebc8d8
Fixed tests
aminya Jan 1, 2020
c36f97e
Addressing review comments
aminya Jan 6, 2020
5937285
create-pull-request@v2.1.0
aminya Jan 13, 2020
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
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ JLD = "4138dd39-2aa7-5051-a626-17a0bb65d9c8"
MatLang = "05b439c0-bb3c-11e9-1d8d-1f0a9ebca87a"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Pkg", "ColorTypes", "Test", "FixedPointNumbers", "JLD", "SparseArrays", "MatLang"]
test = ["Pkg", "ColorTypes", "Test", "FixedPointNumbers", "JLD", "SparseArrays", "MatLang", "Suppressor"]
2 changes: 1 addition & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ makedocs(
prettyurls = get(ENV, "CI", nothing) == "true"
),
modules = [SnoopCompile],
pages = ["index.md", "snoopi.md", "snoopc.md", "userimg.md", "reference.md"]
pages = ["index.md", "snoopi.md", "snoopc.md", "userimg.md", "bot.md", "reference.md"]
)

deploydocs(
Expand Down
158 changes: 158 additions & 0 deletions docs/src/bot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# SnoopCompile Bot (EXPERIMENTAL)

You can use SnoopCompile bot to automatically and continuously create precompile files.

One should add 3 things to a package to make the bot work:

----------------------------------


- Workflow file:

create a workflow file with this path in your repository `.github/workflows/SnoopCompile.yml` and use the following content:

```yaml
name: SnoopCompile

on:
- push


jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
julia-version: ['nightly']
aminya marked this conversation as resolved.
Show resolved Hide resolved
julia-arch: [x64]
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@latest
with:
version: ${{ matrix.julia-version }}
- name: Install dependencies
run: julia --project -e 'using Pkg; Pkg.instantiate();'
- name : Add SnoopCompile and current package
run: julia -e 'using Pkg; Pkg.add("SnoopCompile"); Pkg.develop(PackageSpec(; path=pwd()));'
- name: Install Test dependencies
run: julia -e 'using SnoopCompile; SnoopCompile.addtestdep()'
- name: Generating precompile files
run: julia --project=@. -e 'include("deps/SnoopCompile/snoopCompile.jl")'
- name: Running Benchmark
run: julia --project=@. -e 'include("deps/SnoopCompile/snoopBenchmark.jl")'

# https://github.com/marketplace/actions/create-pull-request
- name: Create Pull Request
uses: peter-evans/create-pull-request@v2.1.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update precompile_*.jl file
committer: YOUR NAME <yourEmail@something.com> # Change `committer` to your name and your email.
title: '[AUTO] Update precompile_*.jl file'
labels: SnoopCompile
branch: create-pull-request/SnoopCompile
- name: Check output environment variable
run: echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}"
```
`Install Test dependencies` step is only needed if you have test dependencies other than Test. Otherwise, you should comment it. In this case, if your examples or tests have dependencies, you should add a `Test.toml` to your test folder.

```yaml
- name: Install Test dependencies
run: julia -e 'using SnoopCompile; SnoopCompile.addtestdep()'
```

For example for MatLang package:

[Link](https://github.com/juliamatlab/MatLang/blob/master/.github/workflows/SnoopCompile.yml)

----------------------------------


- Precompile script

Add a `snoopCompile.jl` file under `deps/SnoopCompile`. The content of the file should be a script that "exercises" the functionality you'd like to precompile. One option is to use your package's `"runtests.jl"` file, or you can write a custom script for this purpose.


For example, some examples that call the functions:

```julia
using SnoopCompile
@snoopiBot "MatLang" begin
using MatLang
examplePath = joinpath(dirname(dirname(pathof(MatLang))), "examples")
include(joinpath(examplePath,"Language_Fundamentals", "usage_Entering_Commands.jl"))
include(joinpath(examplePath,"Language_Fundamentals", "usage_Matrices_and_Arrays.jl"))
include(joinpath(examplePath,"Language_Fundamentals", "Data_Types", "usage_Numeric_Types.jl"))
end
```
[Ref]( https://github.com/juliamatlab/MatLang/blob/master/deps/SnoopCompile/snoopCompile.jl)

or if you do not have additional examples, you can use your runtests.jl file using this syntax:

```julia
using SnoopCompile
# using runtests:
@snoopiBot "MatLang"
```

[Also look at this](https://timholy.github.io/SnoopCompile.jl/stable/snoopi/#Precompile-scripts-1)

----------------------------------

- Include precompile signatures

Two lines of (commented) code that includes the precompile file in your main module.

It is better to have these lines commented to continuously develop and change your package offline. snoopiBot will find these lines of code and will uncomment them in the created pull request. If they are not commented the bot will leave it as is in the pull request:

```julia
# include("../deps/SnoopCompile/precompile/precompile_MatLang.jl")
# _precompile_()
```

[Ref](https://github.com/juliamatlab/MatLang/blob/072ff8ed9877cbb34f8583ae2cf928a5df18aa0c/src/MatLang.jl#L26)


----------------------------------


## Benchmark

To measure the effect of adding precompile files. Add a `snoopBenchmark.jl`. The content of this file can be the following:

Benchmarking the load infer time
```julia
println("loading infer benchmark")
@snoopiBench "MatLang" using MatLang
```

Benchmarking the example infer time
```julia
println("examples infer benchmark")
@snoopiBench "MatLang" begin
using MatLang
examplePath = joinpath(dirname(dirname(pathof(MatLang))), "examples")
# include(joinpath(examplePath,"Language_Fundamentals", "usage_Entering_Commands.jl"))
include(joinpath(examplePath,"Language_Fundamentals", "usage_Matrices_and_Arrays.jl"))
include(joinpath(examplePath,"Language_Fundamentals", "Data_Types", "usage_Numeric_Types.jl"))
end
```

Benchmarking the tests:
```julia
@snoopiBench "MatLang"
```
[Ref](https://github.com/juliamatlab/MatLang/blob/master/deps/SnoopCompile/snoopBenchmark.jl)


To run the benchmark online, add the following to your yaml file after `Generating precompile files` step:

```yaml
- name: Running Benchmark
run: julia --project=@. -e 'include("deps/SnoopCompile/snoopBenchmark.jl")'
```
44 changes: 1 addition & 43 deletions src/SnoopCompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,48 +18,6 @@ include("snoopc.jl")
include("parcel_snoopc.jl")

include("write.jl")

"""
timesum(snoop)
Calculates and prints the total time measured by a snoop macro
# Examples
Julia can cache inference results so to measure the effect of adding _precompile_() sentences generated by snoopi to your package, use the fllowing benchmark. This benchmark measures inference time taken during loading and running of a package.
- dev your package
- comment the precompile part of your package (`include()` and `_precompile_()`)
- run the following benchmark
- restart Julia
- uncomment the precompile part of your package (`include()` and `_precompile_()`)
- run the following benchmark
- restart Julia
## Benchmark
```julia
using SnoopCompile
println("Package load time:")
loadSnoop = @snoopi using MatLang
timesum(loadSnoop)
println("Running Examples/Tests:")
runSnoop = @snoopi begin
using MatLang
include(joinpath(dirname(dirname(pathof(MatLang))),"test","runtests.jl"))
end
timesum(runSnoop)
```
"""
function timesum(snoop::Vector{Tuple{Float64, Core.MethodInstance}})
if isempty(snoop)
return 0.0
else
return sum(first, snoop)
end
end
include("bot.jl")

end # module
30 changes: 30 additions & 0 deletions src/bot.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export precompile_activator, precompile_deactivator, precompile_pather, @snoopiBot, @snoopiBench, BotConfig

const UStrings = Union{AbstractString,Regex,AbstractChar}
################################################################
"""
BotConfig
Config object that holds the options and configuration for the SnoopCompile bot. This object is fed to the `@snoopiBot`.
# Arguments:
- `packageName::String`
- `subst::Vector{Pair{UStrings, UStrings}}` : to replace a packages precompile setences with another's package like `["ImageTest" => "Images"]`
- `blacklist::Vector{UStrings}` : to remove some precompile sentences
`const UStrings == Union{AbstractString,Regex,AbstractChar}` # every string like type that `replace()` has a method for.
"""
struct BotConfig
packageName::String
subst::Vector{Pair{T1, T2}} where {T1<:UStrings, T2 <: UStrings}
blacklist::Vector{T3} where {T3<:UStrings}
end

function BotConfig(packageName::String; subst::Vector{Pair{T1, T2}} where {T1<:UStrings, T2 <: UStrings} = Vector{Pair{String, String}}(), blacklist::Vector{T3} where {T3<:UStrings}= String[])
return BotConfig(packageName, subst, blacklist)
end

include("bot/botutils.jl")
include("bot/precompileInclude.jl")
include("bot/snoopiBot.jl")
include("bot/snoopiBench.jl")
18 changes: 18 additions & 0 deletions src/bot/botutils.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
################################################################
import Pkg
"""
Should be removed once Pkg allows adding test dependencies to the current environment
Used in Github Action workflow yaml file
"""
function addtestdep()
if isfile("test/Test.toml")
testToml = Pkg.Types.parse_toml("test/Test.toml")
else
error("please add a Test.toml to the /test directory for test dependencies")
aminya marked this conversation as resolved.
Show resolved Hide resolved
end

for (name, uuid) in testToml["deps"]
Pkg.add(Pkg.PackageSpec(name = name, uuid = uuid))
end
end
118 changes: 118 additions & 0 deletions src/bot/precompileInclude.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

"""
precompile_pather(packageName::String)
To get the path of precompile_packageName.jl file
Written exclusively for SnoopCompile Github actions.
# Examples
```julia
precompilePath, precompileFolder = precompile_pather("MatLang")
```
"""
function precompile_pather(packageName::String)
return "\"../deps/SnoopCompile/precompile/precompile_$packageName.jl\"",
"$(pwd())/deps/SnoopCompile/precompile/"
end

precompile_pather(packageName::Symbol) = precompile_pather(string(packageName))
precompile_pather(packageName::Module) = precompile_pather(string(packageName))

################################################################

function precompile_regex(precompilePath)
# https://stackoverflow.com/questions/3469080/match-whitespace-but-not-newlines
# {1,} for any number of spaces
c1 = Regex("#[^\\S\\r\\n]{0,}include\\($(precompilePath)\\)")
c2 = r"#\s{0,}_precompile_\(\)"
a1 = "include($precompilePath)"
a2 = "_precompile_()"
return c1, c2, a1, a2
end
################################################################

"""
precompile_activator(packagePath, precompilePath)
Activates precompile of a package by adding or uncommenting include() of *.jl file generated by SnoopCompile and _precompile_().
packagePath is the same as `pathof`. However, `pathof(module)` isn't used to prevent loadnig the package.
Written exclusively for SnoopCompile Github actions.
"""
function precompile_activator(packagePath::String, precompilePath::String)

packageText = Base.read(packagePath, String)

c1, c2, a1, a2 = precompile_regex(precompilePath)

# Checking availability of _precompile_ code
commented = occursin(c1, packageText) && occursin(c2, packageText)
available = occursin(a1, packageText) && occursin(a2, packageText)

if commented
packageEdited = foldl(replace,
(
c1 => a1,
c2 => a2,
),
init = packageText)

Base.write(packagePath, packageEdited)

println("precompile is activated")
elseif available
# do nothing
println("precompile is already activated")
else
# TODO: add code automatiaclly
error(""" add the following codes into your PackageName.jl file under src folder:
#include($precompilePath)
#_precompile_()
""")
end

end

"""
precompile_deactivator(packagePath, precompilePath)
Deactivates precompile of a package by commenting include() of *.jl file generated by SnoopCompile and _precompile_().
packagePath is the same as `pathof`. However, `pathof(module)` isn't used to prevent loadnig the package.
Written exclusively for SnoopCompile Github actions.
"""
function precompile_deactivator(packagePath::String, precompilePath::String)

packageText = Base.read(packagePath, String)

c1, c2, a1, a2 = precompile_regex(precompilePath)

# Checking availability of _precompile_ code
commented = occursin(c1, packageText) && occursin(c2, packageText)
available = occursin(a1, packageText) && occursin(a2, packageText)

if available && !commented
packageEdited = foldl(replace,
(
a1 => "#"*a1,
a2 => "#"*a2,
),
init = packageText)

Base.write(packagePath, packageEdited)

println("precompile is deactivated")
elseif commented
# do nothing
println("precompile is already deactivated")
else
# TODO: add code automatiaclly
error(""" add the following codes into your PackageName.jl file under src folder:
#include($precompilePath)
#_precompile_()
""")
end

end
Loading