GitHub Hành động shell python

GitHub Actions cung cấp một cách để tự động hóa quy trình phát triển phần mềm của bạn trên GitHub. Điều này bao gồm các tác vụ CI/CD truyền thống trên cả ba hệ điều hành chính như chạy bộ thử nghiệm, xây dựng ứng dụng và xuất bản gói. Nhưng nó cũng bao gồm lời chào tự động dành cho những người đóng góp mới, gắn nhãn các yêu cầu kéo dựa trên các tệp đã thay đổi hoặc thậm chí

Ngoài ra, GitHub Actions miễn phí cho các dự án mã nguồn mở 🎉

Trong Tác vụ GitHub, bạn tạo Quy trình công việc, các quy trình công việc này được tạo thành từ và các Công việc này được tạo thành từ

Các bước có thể chạy lệnh, chạy tác vụ thiết lập hoặc chạy một hành động trong kho lưu trữ của bạn, kho lưu trữ công cộng hoặc một hành động được xuất bản trong sổ đăng ký Docker. Không phải tất cả các bước chạy hành động, nhưng tất cả các hành động chạy như một bước

Trong trường hợp này, Hành động là một đoạn mã sẽ được chạy trên cơ sở hạ tầng Hành động GitHub. Nó sẽ có quyền truy cập vào không gian làm việc chứa dự án của bạn và có thể thực hiện bất kỳ tác vụ nào bạn muốn. Nó có thể nhận đầu vào, cung cấp đầu ra và truy cập các biến môi trường. Điều này cho phép bạn xâu chuỗi các Hành động lại với nhau để có khả năng vô tận. Bạn có thể sử dụng các Hành động dựng sẵn từ GitHub Marketplace hoặc tự viết

Trong bài đăng này, chúng tôi sẽ hướng dẫn cách tạo Hành động vùng chứa của riêng bạn bằng Python và xuất bản nó lên GitHub Marketplace để người khác sử dụng

Để tạo cho mình một khởi đầu thuận lợi, chúng ta sẽ sử dụng mẫu

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
3 này mà tôi đã xuất bản trên GitHub. Chúng tôi sẽ nói qua từng tệp chúng tôi cần tạo để bạn không nhất thiết phải sử dụng nó, nhưng nó sẽ giúp cuộc sống của chúng tôi dễ dàng hơn một chút

Sao chép mẫu

Để bắt đầu, hãy truy cập mẫu trên GitHub và nhấn nút “Sử dụng mẫu này” màu xanh lục lớn

GitHub Hành động shell python

Đặt cho Hành động mới của bạn một tên repo và một mô tả tùy chọn. Trong ví dụ này, chúng tôi sẽ thực hiện một Hành động tạo tệp YAML, vì vậy tôi sẽ đặt tên cho nó là

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
4

GitHub Hành động shell python

Điều này sẽ cung cấp cho chúng tôi một kho lưu trữ mới về cơ bản là một nhánh của mẫu

GitHub Hành động shell python

Ngoài ra, mẫu của chúng tôi có một vài Tác vụ GitHub được định cấu hình để kiểm tra Hành động (thu hút đệ quy Hành động GitHub). Chúng tôi có thể truy cập tab “Hành động” và hy vọng sẽ thấy cả hai bài kiểm tra đã vượt qua

GitHub Hành động shell python

Chúng ta sẽ quay lại các thử nghiệm này sau

Cuối cùng, về phía GitHub, bây giờ hãy sao chép kho lưu trữ của chúng tôi cục bộ

$ git clone 

GitHub Hành động shell python

Viết mã hành động của chúng tôi

Nếu bạn tìm trong dự án, bạn sẽ tìm thấy một vài tệp. Hai thứ chúng ta sẽ tập trung vào bây giờ là

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5 và
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
6

Dockerfile

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5 đây là bản dựng Python Docker khá tối giản

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]

Chúng tôi bắt đầu với một thùng chứa

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
8 dựa trên
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
9. Chúng tôi sao chép mã của mình và chạy
$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml                               latest              4eb311726658        9 seconds ago       54.1MB
0 để cài đặt các phụ thuộc của bạn vào thư mục ứng dụng của chúng tôi

Sau đó, chúng tôi sử dụng một bước xây dựng nhiều tầng để chuyển sang vùng chứa không phân phối rất tối thiểu. Hình ảnh bộ chứa này không có hệ điều hành và chỉ chứa một số thứ như chứng chỉ SSL để đảm bảo ứng dụng của chúng tôi có thể hoạt động chính xác khi giao tiếp với các dịch vụ web một cách an toàn

Chúng tôi sao chép mã Python và các phần phụ thuộc từ bộ chứa

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
8 và đặt nó làm lệnh thời gian chạy của chúng tôi

Kết quả của việc này là một hình ảnh vùng chứa khá nhỏ. Ví dụ chúng tôi có ở đây nên xây dựng tới khoảng 50MB

$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml                               latest              4eb311726658        9 seconds ago       54.1MB

Tất nhiên, điều này sẽ tăng lên một chút khi chúng tôi thêm nhiều mã hơn, nhưng nó vẫn nhỏ so với một thùng chứa có bản phân phối linux đầy đủ trong đó. Đây cũng là một cách rất an toàn để xây dựng các thùng chứa vì chúng tôi đã giảm đáng kể bề mặt tấn công của mình nếu một kẻ xấu khai thác ứng dụng của chúng tôi, họ thậm chí sẽ không có vỏ để xâm nhập vào thùng chứa

Bạn có thể tạo các vùng chứa nhỏ hơn nữa bằng cách sử dụng ngôn ngữ được biên dịch tĩnh như Go. Có một hướng dẫn và mẫu cho điều đó quá

chủ yếu. py

Chúng ta cũng hãy dành một phút để khám phá tệp

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
6 ví dụ mà chúng tôi có ở đây trước khi thay thế nó bằng mã linting YAML của chúng tôi

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()

Điều này dài hơn một chút so với ví dụ thế giới xin chào Python tiêu chuẩn

Chúng tôi đã bọc logic của mình trong một hàm

$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml                               latest              4eb311726658        9 seconds ago       54.1MB
3 và đang gọi nó từ bên trong một khối
$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml                               latest              4eb311726658        9 seconds ago       54.1MB
4. Chúng tôi cũng đang nhập
$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml                               latest              4eb311726658        9 seconds ago       54.1MB
5 một chút không cần thiết ở đây chỉ để chứng minh rằng cài đặt phụ thuộc của chúng tôi trong
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5 đã hoạt động, chúng tôi sẽ không thực sự sử dụng nó

Dòng đầu tiên trong hàm

$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml                               latest              4eb311726658        9 seconds ago       54.1MB
3 của chúng tôi đang nhận đầu vào Hành động GitHub từ một biến môi trường. Các hành động của GitHub có thể nhận các đối số bằng cách sử dụng câu lệnh
$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml                               latest              4eb311726658        9 seconds ago       54.1MB
8 và những đối số này được hiển thị thông qua các biến môi trường có tên
$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml                               latest              4eb311726658        9 seconds ago       54.1MB
9 theo sau là tên đối số viết hoa toàn bộ

name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world

Trong cấu hình trên, biến

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
0 sẽ được tạo với giá trị
import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
1. Sau đó chúng tôi có thể lấy cái này trong mã của chúng tôi

Dòng mã Python tiếp theo đang định dạng biến này thành một chuỗi đầu ra, không có gì lạ mắt ở đây ngoài chuỗi f (chuỗi f woo 🎉). Điều này có nghĩa là chuỗi đầu ra của chúng tôi bây giờ sẽ là

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
2 trong quy trình làm việc ví dụ nhỏ của chúng tôi

Sau đó, cuối cùng nó được in ra thiết bị xuất chuẩn bằng một số cú pháp Tác vụ GitHub cụ thể. Bạn có thể chuyển thông tin trở lại quy trình làm việc của GitHub Actions với một số cụm từ kiểm soát nhất định. Đây là ví dụ về thiết lập đầu ra của Hành động bằng cách in

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
3

Điều này có nghĩa là chúng ta có thể truy cập giá trị đầu ra của mình trong các Tác vụ GitHub khác sau này trong quy trình làm việc

Viết kẻ nói dối YAML của chúng tôi

Bài viết này không nhằm mục đích hướng dẫn Python, vì vậy chúng tôi sẽ không đi vào chi tiết mã ở đây. Nhưng đây là một phác thảo chung

Ứng dụng của chúng tôi sẽ lấy một số đầu vào

  • import os
    import requests  # noqa We are just importing this to prove the dependency installed correctly
    
    
    def main():
        my_input = os.environ["INPUT_MYINPUT"]
    
        my_output = f"Hello {my_input}"
    
        print(f"::set-output name=myOutput::{my_output}")
    
    
    if __name__ == "__main__":
        main()
    
    4 vào tệp chúng tôi muốn lint liên quan đến thư mục gốc của dự án
  • Liệu chúng ta có nên
    import os
    import requests  # noqa We are just importing this to prove the dependency installed correctly
    
    
    def main():
        my_input = os.environ["INPUT_MYINPUT"]
    
        my_output = f"Hello {my_input}"
    
        print(f"::set-output name=myOutput::{my_output}")
    
    
    if __name__ == "__main__":
        main()
    
    5 trong phần linting của mình hay không (lỗi khi cảnh báo)

Sau đó, chúng tôi sẽ sử dụng thư viện của bên thứ ba

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
6 để cắt xén mã, đưa ra các lỗi và cảnh báo bằng cách sử dụng các cụm từ kiểm soát và thoát bằng một dấu ____13_______7 hoặc
import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
8 tùy thuộc vào việc tệp có được ghép nối thành công hay không

Hành động của chúng tôi cũng sẽ in một đầu ra báo cáo số lượng cảnh báo nếu chúng tôi vượt qua linting

import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()

Ngoài ra, khi chúng tôi đang sử dụng phụ thuộc

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
6 trong mã của mình, chúng tôi cần cập nhật
name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
0 trong
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5 của mình

RUN pip install --target=/app yamllint

Cập nhật hành động. yml

Bây giờ chúng tôi có một số mã, chúng tôi cần cập nhật tệp

name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
2 của mình để cho GitHub Actions cách chạy mã của chúng tôi và mong đợi đầu vào và đầu ra nào

name: "YAML file linter"
description: "Lint a YAML file in your project"
author: "Jacob Tomlinson"
inputs:
  path:
    description: "Path to the YAML file to be linted"
    required: true
  strict:
    description: "Run the linter in strict mode (error on warnings)"
    required: false
    default: false
outputs:
  warnings:
    description: "Number of warnings raised if lint was successful"
runs:
  using: "docker"
  image: "Dockerfile"

Chúng tôi bắt đầu ở đây bằng cách đặt một số siêu dữ liệu để mô tả Hành động, hành động đó làm gì và ai đã viết hành động đó

Sau đó, chúng tôi liệt kê các đầu vào và đầu ra của mình, đưa ra mô tả cho chúng, đặt bất kỳ giá trị mặc định nào và liệu chúng có phải là tùy chọn hay không. Đối với Hành động lint YAML của chúng tôi, chúng tôi đã chỉ định đầu vào

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
4 để chúng tôi có thể đặt tệp nào thành lint, đây không phải là tùy chọn. Chúng tôi cũng có đầu vào
import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
5 nơi chúng tôi có thể tùy ý đặt kẻ nói dối của mình thành lỗi ở chế độ cảnh báo. Sau đó, chúng tôi cho GitHub Actions biết để mong đợi kết quả đầu ra là
name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
5 từ mã của chúng tôi

Cuối cùng, chúng tôi cho GitHub biết cách chạy Hành động của chúng tôi. Trong trường hợp này, chúng tôi đang sử dụng

name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
6 và chúng tôi có thể chỉ định một hình ảnh theo tên hoặc đặt nó thành
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5, điều này sẽ khiến các Tác vụ GitHub tự xây dựng hình ảnh của chúng tôi khi chạy Hành động

Một cải tiến tốt đẹp trong tương lai có thể là thiết lập Triển khai liên tục để xây dựng và đẩy hình ảnh của chúng tôi lên Docker Hub. Điều này sẽ cho phép chúng tôi tham chiếu hình ảnh của mình theo tên ở đây và nó sẽ được tạo sẵn, giúp chúng tôi tiết kiệm thời gian bất cứ khi nào Hành động của chúng tôi được sử dụng. Hiện tại, hình ảnh của chúng tôi mất khoảng 7 giây để xây dựng, điều này không tệ lắm, nhưng nếu Hành động của chúng tôi phức tạp hơn thì sẽ lâu hơn

Cập nhật README

Chúng tôi cũng nên cập nhật tệp

name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
8 của mình để phản ánh cấu hình này nhằm giúp mọi người hiểu cách sử dụng Hành động của chúng tôi

Tôi sẽ không bao gồm tệp này ở đây vì nó hơi dài nhưng bạn có thể kiểm tra nó trên GitHub

Tôi khuyên bạn nên bao gồm các phần sau

  • Huy hiệu cho biết đó là Hành động trên thị trường
  • Tổng quan về những gì Hành động làm
  • Bảng đầu vào và đầu ra
  • Ví dụ về các cách khác nhau để sử dụng Hành động của bạn

Viết bài kiểm tra

Bây giờ chúng ta đã có mã, cấu hình và tài liệu cho Hành động của mình, chúng ta nên kiểm tra nó

thử nghiệm cục bộ

Trong khi viết mã, tôi đã thực hiện một số thử nghiệm cục bộ. Tôi đã tạo một tệp mới trong thư mục có tên là

name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
9 có tên là
import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()
0

Sau đó, tôi chạy mã Hành động của mình với các biến môi trường được đặt nội tuyến để kiểm tra sự thay đổi

$ INPUT_PATH=tests/valid.yaml INPUT_STRICT=false python main.py
::warning file=tests/valid.yaml,line=1,col=1::missing document start "---" (document-start)
::set-output name=warnings::1

Chạy cái này đã báo cáo một cảnh báo bằng cách sử dụng định dạng cụm từ kiểm soát Hành động GitHub và báo cáo các cảnh báo

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
8 dưới dạng đầu ra. Tuyệt vời

Tôi cũng đã tạo một tệp không hợp lệ có tên là

import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()
2 để kiểm tra xem linting có bị lỗi không

$ INPUT_PATH=tests/invalid.yaml INPUT_STRICT=false python main.py
::warning file=tests/invalid.yaml,line=1,col=1::missing document start "---" (document-start)
::error file=tests/invalid.yaml,line=4,col=1::syntax error: could not find expected ':' (None)
exit 1

Ở đây, chúng ta có thể thấy rằng đã xảy ra lỗi và lệnh đã thoát với trạng thái

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
8, điều này sẽ khiến quy trình làm việc của GitHub Actions không thành công. siêu

Bây giờ, hãy thiết lập một số Tích hợp liên tục để kiểm tra mã mỗi khi chúng tôi đẩy mạnh thay đổi hoặc ai đó mở Yêu cầu kéo. Chúng tôi có thể thiết lập điều này bằng GitHub Actions

Thử nghiệm bản dựng

Điều đơn giản nhất chúng ta có thể làm là kiểm tra xem mã Python của chúng ta có hợp lệ không. Khi chúng tôi sử dụng mẫu

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
3, chúng tôi đã có một Quy trình công việc được định cấu hình trong
import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()
5, quy trình này sẽ hoạt động tốt cho mã mới của chúng tôi

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
0

Quy trình công việc này chạy ba bước. Chúng tôi có một môi trường Python hoạt động với

import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()
6 và sau đó chúng tôi kiểm tra mã của mình với
import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()
7. Đây đều là những Hành động chính thức do GitHub cung cấp và bạn cũng có thể tìm nguồn cho cả hai Hành động này trên GitHub

Sau đó, chúng tôi có một bước

import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()
8 nơi chúng tôi đã cung cấp một tập lệnh shell ngắn để cài đặt
import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()
9 và sau đó sử dụng nó để tạo mã của chúng tôi

Chúng tôi không cần thay đổi bất cứ điều gì ở đây vì điều này là đủ cho hầu hết các ứng dụng Python đơn giản

Thử nghiệm hội nhập

Bây giờ chúng tôi đã kiểm tra mã của mình có hợp lệ hay không, chúng tôi cũng có thể kiểm tra xem Hành động của chúng tôi có chạy chính xác trên cơ sở hạ tầng Hành động GitHub không. Chúng tôi có thể làm điều này bằng cách để Hành động của chúng tôi tự chạy theo các yêu cầu đẩy và kéo

Khởi động Hành động này là một trong những tính năng yêu thích của tôi về Hành động GitHub vì chúng tôi có thể thực hiện kiểm tra tích hợp toàn diện từ đầu đến cuối mà không cần bất kỳ thiết lập phức tạp nào

Có một ví dụ trong

RUN pip install --target=/app yamllint
0 mà chúng tôi sẽ sửa đổi để giống như thế này

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
1

Trong quy trình làm việc này, chúng tôi bắt đầu bằng cách kiểm tra mã Hành động của mình. Sau đó, chúng tôi tự chạy Hành động của riêng mình và đặt đầu vào giống như chúng tôi đang thực hiện cục bộ (lưu ý rằng chúng tôi đã bỏ qua đầu vào

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
5 vì đây là tùy chọn và giá trị mặc định là ổn)

Hành động này sẽ làm hỏng tệp

import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()
0 của chúng tôi và đưa ra một cảnh báo

Sau đó, chúng ta có một Hành động

import os
import sys

from yamllint import linter
from yamllint.config import YamlLintConfig


def main():
    yaml_path = os.environ["INPUT_PATH"]
    strict = os.environ["INPUT_STRICT"] == "true"
    conf = YamlLintConfig("extends: default")
    warning_count = 0

    with open(yaml_path) as f:
        problems = linter.run(f, conf, yaml_path)

    for problem in problems:

        if problem.level == "warning" and strict:
            problem.level = "error"

        print(
            f"::{problem.level} file={yaml_path},line={problem.line},"
            f"col={problem.column}::{problem.desc} ({problem.rule})"
        )

        if problem.level == "warning":
            warning_count = warning_count + 1

        if problem.level == "error":
            sys.exit(1)

    print(f"::set-output name=warnings::{warning_count}")

    sys.exit(0)


if __name__ == "__main__":
    main()
8 để kiểm tra xem Hành động của chúng ta có hoạt động như chúng ta mong đợi hay không bằng cách kiểm tra xem đầu ra
name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
5 có hiển thị chính xác
import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
8 hay không

Đẩy lên GitHub

Tiếp theo, chúng ta có thể đẩy Hành động của mình lên GitHub và chạy kiểm tra để đảm bảo mọi thứ hoạt động như mong đợi

FROM python:3-slim AS builder
ADD . /app
WORKDIR /app

# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests

# A distroless container image with Python and some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
2

Chúng tôi có thể chuyển đến tab Hành động trên kho lưu trữ của mình và xem quy trình công việc

RUN pip install --target=/app yamllint
6 và
RUN pip install --target=/app yamllint
7 của chúng tôi chạy

GitHub Hành động shell python

Bước lint của chúng tôi đã qua, vì vậy chúng tôi chắc chắn đã đẩy mã Python hợp lệ

GitHub Hành động shell python

Hoan hô bài kiểm tra tích hợp của chúng tôi cũng đã vượt qua. Chúng tôi có thể xem kết quả đầu ra từ Hành động của mình và kiểm tra để biết rằng Hành động của chúng tôi đã đưa ra cảnh báo màu cam như mong đợi và sau đó báo cáo chính xác các cảnh báo

import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
8. Lưu ý rằng giá trị mặc định
import os
import requests  # noqa We are just importing this to prove the dependency installed correctly


def main():
    my_input = os.environ["INPUT_MYINPUT"]

    my_output = f"Hello {my_input}"

    print(f"::set-output name=myOutput::{my_output}")


if __name__ == "__main__":
    main()
5 cũng được điền cho chúng tôi

Xuất bản ra thị trường

Bây giờ chúng tôi đã tạo Hành động của mình và xác minh rằng nó hoạt động như mong đợi, chúng tôi có thể xuất bản nó lên Thị trường để những người khác có thể tìm thấy nó

Về mặt kỹ thuật, bất kỳ ai cũng có thể sử dụng Hành động của chúng tôi ngay bây giờ, chúng tôi đã tự sử dụng nó trong thử nghiệm tích hợp của mình. Nhưng chi nhánh

name: "YAML file linter"
description: "Lint a YAML file in your project"
author: "Jacob Tomlinson"
inputs:
  path:
    description: "Path to the YAML file to be linted"
    required: true
  strict:
    description: "Run the linter in strict mode (error on warnings)"
    required: false
    default: false
outputs:
  warnings:
    description: "Number of warnings raised if lint was successful"
runs:
  using: "docker"
  image: "Dockerfile"
0 là một mục tiêu di động và chúng tôi muốn giúp mọi người dễ dàng tìm thấy nó hơn một chút

Để xuất bản nó, chúng tôi cần thực hiện phát hành GitHub. Chúng tôi có thể thực hiện việc này theo cách thông thường thông qua trang phát hành, nhưng GitHub có thể đã phát hiện ra rằng chúng tôi đã viết một Hành động và bao gồm lời nhắc trên repo của chúng tôi. Nhấp vào nút "Dự thảo bản phát hành" trên hộp thoại "Xuất bản hành động này lên thị trường" hoặc đi tới trang phát hành và nhấp vào nó từ đó

Nếu bạn đã theo dõi hộp thoại xuất bản, hộp kiểm “Xuất bản hành động này lên GitHub Marketplace” đã được chọn. Nếu bạn truy cập trang phát hành, bạn sẽ cần tự mình đánh dấu vào ô này

GitHub Hành động shell python

GitHub đã chọn một số thông tin từ tệp

name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
2 của chúng tôi bao gồm tên và mô tả. Nó cũng cảnh báo chúng tôi rằng chúng tôi chưa đặt logo hoặc màu cho Hành động của mình. Đây là cách bạn đặt biểu tượng mà bạn sẽ thấy bên cạnh Hành động của mình trên Thị trường

GitHub Hành động shell python

Hiện tại, chúng tôi sẽ bỏ qua phần của chúng tôi, phần này sẽ chỉ sử dụng các giá trị mặc định nhưng bạn có thể quay lại

name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
2 của mình và đặt phần của bạn theo bất kỳ cách nào bạn muốn

Bạn cũng cần chọn các danh mục mà Hành động của bạn thuộc về. Đối với Hành động kẻ nói dối tệp YAML của chúng tôi, tôi đã sử dụng “Tích hợp liên tục” và “Tiện ích”

Bạn cũng cần đặt thẻ phiên bản. Sở thích của tôi là sử dụng SemVer và bắt đầu từ

name: "YAML file linter"
description: "Lint a YAML file in your project"
author: "Jacob Tomlinson"
inputs:
  path:
    description: "Path to the YAML file to be linted"
    required: true
  strict:
    description: "Run the linter in strict mode (error on warnings)"
    required: false
    default: false
outputs:
  warnings:
    description: "Number of warnings raised if lint was successful"
runs:
  using: "docker"
  image: "Dockerfile"
3

Khi bạn nhấp vào "Xuất bản bản phát hành", bạn sẽ được đưa đến trang phát hành. Bạn nên chú ý rằng ở phía bên trái nó có huy hiệu “Thị trường” nhỏ màu xanh lam, nghĩa là bản phát hành này có sẵn trên Thị trường

GitHub Hành động shell python

Nếu bạn nhấp vào liên kết đó, bạn sẽ được đưa đến trang Thị trường cho hành động mới xuất bản của mình

GitHub Hành động shell python

Danh sách Thị trường sẽ sử dụng

name: My Workflow
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: jacobtomlinson/python-container-action@master
      with:
        myInput: world
8 từ kho lưu trữ của bạn và cung cấp một số liên kết nhanh để mọi người bắt đầu với Hành động của bạn

Phần kết luận

Đó là nó. Chúng tôi đã xuất bản thành công Hành động của mình trên GitHub Marketplace để người khác sử dụng. Họ có thể chỉ định một phiên bản được gắn thẻ để có thể đảm bảo quy trình công việc của họ mang tính quyết định và sử dụng phiên bản đó trong bất kỳ dự án nào họ muốn

Vì vậy, để nhanh chóng tóm tắt

  • Chúng tôi đã nhân bản mẫu
    FROM python:3-slim AS builder
    ADD . /app
    WORKDIR /app
    
    # We are installing a dependency here directly into our app source dir
    RUN pip install --target=/app requests
    
    # A distroless container image with Python and some basics like SSL certificates
    # https://github.com/GoogleContainerTools/distroless
    FROM gcr.io/distroless/python3-debian10
    COPY --from=builder /app /app
    WORKDIR /app
    ENV PYTHONPATH /app
    CMD ["/cmd/main.py"]
    
    3
  • Chúng tôi đã viết một ứng dụng tiện ích nhỏ bằng Python để tách các tệp YAML
  • Chúng tôi đã cập nhật tệp
    name: My Workflow
    on: [push, pull_request]
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
        - uses: jacobtomlinson/python-container-action@master
          with:
            myInput: world
    
    2 của mình để mô tả đầu vào và đầu ra của ứng dụng của chúng tôi
  • Chúng tôi đã cập nhật tài liệu về
    name: My Workflow
    on: [push, pull_request]
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
        - uses: jacobtomlinson/python-container-action@master
          with:
            myInput: world
    
    8 của mình để giúp mọi người bắt đầu với Hành động của chúng tôi
  • Chúng tôi đã viết một số thử nghiệm để đảm bảo rằng Hành động của chúng tôi hoạt động như mong đợi khi chạy trong Quy trình hành động GitHub
  • Cuối cùng, chúng tôi đã xuất bản nó lên GitHub Marketplace bằng cách phát hành

Bạn có thể xem Hành động kẻ nói dối tệp YAML tại đây và thậm chí sử dụng nó trong quy trình làm việc của riêng bạn

Nếu bạn thấy hướng dẫn này hữu ích hoặc thậm chí xuất bản Hành động GitHub của riêng bạn trên Thị trường, hãy cho tôi biết trong các nhận xét bên dưới hoặc trên phương tiện truyền thông xã hội

Tác vụ GitHub có thể chạy tập lệnh Python không?

Chạy hành động tập lệnh Python. Viết tập lệnh Python trong tệp quy trình Hành động . Hành động này cho phép bạn xác định tập lệnh Python tùy chỉnh bên trong tệp YAML của quy trình làm việc. Viết mã Python của bạn làm đối số tập lệnh và sử dụng tính năng chuỗi nhiều dòng YAML để xác định tập lệnh nhiều dòng.

GitHub Actions sử dụng Shell gì?

Lõi PowerShell. GitHub nối thêm tiện ích mở rộng. ps1 thành tên tập lệnh của bạn. Nếu trình chạy Windows tự lưu trữ của bạn chưa cài đặt PowerShell Core thì PowerShell Desktop sẽ được sử dụng thay thế.

Hành động GitHub có phải là YAML không?

Tác vụ GitHub sử dụng cú pháp YAML để xác định quy trình làm việc . Mỗi quy trình làm việc được lưu trữ dưới dạng một tệp YAML riêng trong kho lưu trữ mã của bạn, trong một thư mục có tên. github/quy trình công việc. Bạn có thể tạo một quy trình công việc mẫu trong kho lưu trữ của mình để tự động kích hoạt một loạt lệnh bất cứ khi nào mã được đẩy.

Bạn có thể chạy tập lệnh trên GitHub không?

Thêm tập lệnh vào quy trình làm việc của bạn . Để biết thêm thông tin, hãy xem "Cú pháp quy trình làm việc cho Tác vụ GitHub. "you can store the script in your repository and supply the path and shell type. For more information, see "Workflow syntax for GitHub Actions."