Merge branch 'origin-master'

This commit is contained in:
Michael Steinert
2015-11-17 12:14:30 -06:00
7 changed files with 480 additions and 0 deletions
+2
View File
@@ -22,3 +22,5 @@ _testmain.go
*.exe
*.test
*.prof
drone-pypi
+19
View File
@@ -0,0 +1,19 @@
Use the PyPI plugin to deploy a Python package to a PyPI server.
* **repository** - The repository name (optional)
* **username** - The username to login with (optional)
* **password** - A password to login with (optional)
* **distributions** - A list of distribution types to deploy (optional)
The following is an example configuration for your .drone.yml:
```yaml
deploy:
pypi:
repository: https://pypi.python.org/pypi
username: guido
password: secret
distributions:
- sdist
- bdist_wheel
```
+14
View File
@@ -0,0 +1,14 @@
FROM alpine:3.2
RUN apk add -U \
ca-certificates \
py-pip \
python \
&& rm -rf /var/cache/apk/* \
&& pip install --no-cache-dir --upgrade \
pip \
setuptools
ADD drone-pypi /bin/
ENTRYPOINT ["/bin/drone-pypi"]
+63
View File
@@ -1,2 +1,65 @@
# drone-pypi
Drone plugin for publishing to the Python package index
## Usage
Upload a source distribution to PyPI
```sh
./drone-pypi <<EOF
{
"workspace": {
"path": "/drone/my-module-py"
}
"vargs": {
"username": "guido",
"password": "secret"
}
}
EOF
```
Upload a source distribution and a wheel to PyPI
```sh
./drone-pypi <<EOF
{
"workspace": {
"path": "/drone/my-module-py"
}
"vargs": {
"distributions": ["sdist", "bdist_wheel"],
"username": "guido",
"password": "secret"
}
}
EOF
```
Upload a source distribution to a private PyPI server, e.g. [simplepypi][]
```sh
./drone-pypi <<EOF
{
"workspace": {
"path": "/drone/my-module-py"
}
"vargs": {
"repository": "https://pypi.example.com"
}
}
EOF
```
[simplepypi]: https://github.com/steiza/simplepypi
## Docker
Build the Docker container using the `netgo` build tag to eliminate
the CGO dependency:
```sh
CGO_ENABLED=0 go build -a -tags netgo
docker build --rm=true -t plugins/drone-pypi .
```
+113
View File
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://web.resource.org/cc/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="110.4211"
height="109.8461"
id="svg2169"
sodipodi:version="0.32"
inkscape:version="0.45.1"
version="1.0"
sodipodi:docbase="/home/bene/Desktop"
sodipodi:docname="dessin-1.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs2171">
<linearGradient
id="linearGradient11301"
inkscape:collect="always">
<stop
id="stop11303"
offset="0"
style="stop-color:#ffe052;stop-opacity:1" />
<stop
id="stop11305"
offset="1"
style="stop-color:#ffc331;stop-opacity:1" />
</linearGradient>
<linearGradient
gradientUnits="userSpaceOnUse"
y2="168.1012"
x2="147.77737"
y1="111.92053"
x1="89.136749"
id="linearGradient11307"
xlink:href="#linearGradient11301"
inkscape:collect="always" />
<linearGradient
id="linearGradient9515"
inkscape:collect="always">
<stop
id="stop9517"
offset="0"
style="stop-color:#387eb8;stop-opacity:1" />
<stop
id="stop9519"
offset="1"
style="stop-color:#366994;stop-opacity:1" />
</linearGradient>
<linearGradient
gradientUnits="userSpaceOnUse"
y2="131.85291"
x2="110.14919"
y1="77.070274"
x1="55.549179"
id="linearGradient9521"
xlink:href="#linearGradient9515"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.24748737"
inkscape:cx="-260.46312"
inkscape:cy="316.02744"
inkscape:document-units="px"
inkscape:current-layer="layer1"
width="131.10236px"
height="184.25197px"
inkscape:window-width="872"
inkscape:window-height="624"
inkscape:window-x="5"
inkscape:window-y="48" />
<metadata
id="metadata2174">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-473.36088,-251.72485)">
<g
id="g1894"
transform="translate(428.42338,184.2561)">
<path
style="opacity:1;color:#000000;fill:url(#linearGradient9521);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
d="M 99.75,67.46875 C 71.718268,67.468752 73.46875,79.625 73.46875,79.625 L 73.5,92.21875 L 100.25,92.21875 L 100.25,96 L 62.875,96 C 62.875,96 44.9375,93.965724 44.9375,122.25 C 44.937498,150.53427 60.59375,149.53125 60.59375,149.53125 L 69.9375,149.53125 L 69.9375,136.40625 C 69.9375,136.40625 69.433848,120.75 85.34375,120.75 C 101.25365,120.75 111.875,120.75 111.875,120.75 C 111.875,120.75 126.78125,120.99096 126.78125,106.34375 C 126.78125,91.696544 126.78125,82.125 126.78125,82.125 C 126.78125,82.124998 129.04443,67.46875 99.75,67.46875 z M 85,75.9375 C 87.661429,75.937498 89.8125,78.088571 89.8125,80.75 C 89.812502,83.411429 87.661429,85.5625 85,85.5625 C 82.338571,85.562502 80.1875,83.411429 80.1875,80.75 C 80.187498,78.088571 82.338571,75.9375 85,75.9375 z "
id="path8615" />
<path
id="path8620"
d="M 100.5461,177.31485 C 128.57784,177.31485 126.82735,165.1586 126.82735,165.1586 L 126.7961,152.56485 L 100.0461,152.56485 L 100.0461,148.7836 L 137.4211,148.7836 C 137.4211,148.7836 155.3586,150.81787 155.3586,122.53359 C 155.35861,94.249323 139.70235,95.252343 139.70235,95.252343 L 130.3586,95.252343 L 130.3586,108.37734 C 130.3586,108.37734 130.86226,124.03359 114.95235,124.03359 C 99.042448,124.03359 88.421098,124.03359 88.421098,124.03359 C 88.421098,124.03359 73.514848,123.79263 73.514848,138.43985 C 73.514848,153.08705 73.514848,162.6586 73.514848,162.6586 C 73.514848,162.6586 71.251668,177.31485 100.5461,177.31485 z M 115.2961,168.8461 C 112.63467,168.8461 110.4836,166.69503 110.4836,164.0336 C 110.4836,161.37217 112.63467,159.2211 115.2961,159.2211 C 117.95753,159.2211 120.1086,161.37217 120.1086,164.0336 C 120.10861,166.69503 117.95753,168.8461 115.2961,168.8461 z "
style="opacity:1;color:#000000;fill:url(#linearGradient11307);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

+137
View File
@@ -0,0 +1,137 @@
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"github.com/drone/drone-plugin-go/plugin"
)
type Params struct {
Distributions []string `json:"distributions"`
Password *string `json:"password,omitempty"`
Repository *string `json:"repository,omitempty"`
Username *string `json:"username,omitempty"`
}
func main() {
w := plugin.Workspace{}
v := Params{}
plugin.Param("workspace", &w)
plugin.Param("vargs", &v)
plugin.MustParse()
err := deploy(&w, &v)
if err != nil {
log.Fatal(err)
}
}
func deploy(w *plugin.Workspace, v *Params) error {
err := createConfig(v)
if err != nil {
return err
}
err = uploadDist(w, v)
if err != nil {
return err
}
return nil
}
func createConfig(v *Params) error {
f, err := os.Create(path.Join(os.Getenv("HOME"), ".pypirc"))
if err != nil {
return err
}
defer f.Close()
buf := bufio.NewWriter(f)
err = v.WriteConfig(buf)
if err != nil {
return err
}
buf.Flush()
return nil
}
func uploadDist(w *plugin.Workspace, v *Params) error {
cmd, err := v.Upload()
if err != nil {
return err
}
cmd.Dir = w.Path
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
fmt.Println("$", strings.Join(cmd.Args, " "))
err = cmd.Run()
if err != nil {
return err
}
return nil
}
// WriteConfig writes a .pypirc to a supplied io.Writer.
func (v *Params) WriteConfig(w io.Writer) error {
repository := "https://pypi.python.org/pypi"
if v.Repository != nil {
repository = *v.Repository
}
username := "guido"
if v.Username != nil {
username = *v.Username
}
password := "secret"
if v.Password != nil {
password = *v.Password
}
_, err := io.WriteString(w, fmt.Sprintf(`[distutils]
index-servers =
pypi
[pypi]
repository: %s
username: %s
password: %s
`, repository, username, password))
return err
}
// Upload creates a setuptools upload command.
func (v *Params) Upload() (*exec.Cmd, error) {
distributions := []string{"sdist"}
if len(v.Distributions) > 0 {
distributions = v.Distributions
}
args := []string{"python", "setup.py"}
for i := range distributions {
args = append(args, distributions[i])
}
args = append(args, "upload")
args = append(args, "-r")
args = append(args, "pypi")
return command(args)
}
// Command builds a command using a variable length argument list.
func command(args []string) (*exec.Cmd, error) {
name := args[0]
cmd := &exec.Cmd{
Path: name,
Args: args,
}
if filepath.Base(name) == name {
lp, err := exec.LookPath(name)
if err != nil {
return nil, err
}
cmd.Path = lp
}
return cmd, nil
}
+132
View File
@@ -0,0 +1,132 @@
package main
import (
"bytes"
"os"
"strings"
"testing"
"github.com/drone/drone-plugin-go/plugin"
)
func TestDeploy(t *testing.T) {
w := plugin.Workspace{
Path: os.Getenv("DRONE_PYPI_PATH"),
}
repository := os.Getenv("DRONE_PYPI_REPOSITORY")
username := os.Getenv("DRONE_PYPI_USERNAME")
password := os.Getenv("DRONE_PYPI_PASSWORD")
v := Params{
Repository: &repository,
Username: &username,
Password: &password,
Distributions: strings.Split(os.Getenv("DRONE_PYPI_DISTRIBUTIONS"), " "),
}
if w.Path == "" {
t.Skip("DRONE_PYPI_PATH not set")
}
err := deploy(&w, &v)
if err != nil {
t.Error(err)
}
}
func sPtr(s string) *string {
return &s
}
func TestConfig(t *testing.T) {
testdata := []struct {
repository *string
username *string
password *string
exp string
}{
{
nil,
nil,
nil,
`[distutils]
index-servers =
pypi
[pypi]
repository: https://pypi.python.org/pypi
username: guido
password: secret
`,
},
{
sPtr("https://pypi.example.com"),
nil,
nil,
`[distutils]
index-servers =
pypi
[pypi]
repository: https://pypi.example.com
username: guido
password: secret
`,
},
{
nil,
sPtr("jqhacker"),
sPtr("supersecret"),
`[distutils]
index-servers =
pypi
[pypi]
repository: https://pypi.python.org/pypi
username: jqhacker
password: supersecret
`,
},
}
for i, data := range testdata {
v := Params{
Repository: data.repository,
Username: data.username,
Password: data.password,
Distributions: []string{},
}
var b bytes.Buffer
v.WriteConfig(&b)
if b.String() != data.exp {
t.Errorf("Case %d: Expected %s, got %s\n", i, data.exp, b.String())
}
}
}
func TestUpload(t *testing.T) {
testdata := []struct {
distributions []string
exp []string
}{
{
[]string{},
[]string{"python", "setup.py", "sdist", "upload", "-r", "pypi"},
},
{
[]string{"sdist", "bdist_wheel"},
[]string{"python", "setup.py", "sdist", "bdist_wheel", "upload", "-r", "pypi"},
},
}
for i, data := range testdata {
v := Params{Distributions: data.distributions}
c, err := v.Upload()
if err != nil {
t.Error(err)
}
if len(c.Args) != len(data.exp) {
t.Errorf("Case %d: Expected %d, got %d", i, len(data.exp), len(c.Args))
}
for i := range c.Args {
if c.Args[i] != data.exp[i] {
t.Errorf("Case %d: Expected %s, got %s", i, strings.Join(data.exp, " "), strings.Join(c.Args, " "))
}
}
}
}